mxreality.js 78 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015
  1. /*
  2. *Copyright © 2017 mxreality.js authors
  3. */
  4. var VR=function (scene,renderer,container,cameraPara,cameraPosition) {
  5. AVR.initDomStyle(container);
  6. AVR.setCameraPara(this,cameraPara,cameraPosition);
  7. this.vrbox={"radius":2, "widthSegments":180, "heightSegments":180,"width":2,"height":2,"depth":2};
  8. this.scene=scene;
  9. this.renderer=renderer;
  10. this.container=container;
  11. this.video=null;
  12. this.audio=null;
  13. this.videoToolBar=null;
  14. this.autoplayPanoImg=false;
  15. this.clock = new THREE.Clock();
  16. this.VRObject=new THREE.Object3D();
  17. this.defaultAutoHideLeftTime=3;
  18. this.defaultVoiceHideLeftTime=2;
  19. this.defaultVolume=0.3;
  20. this.sliceSegment=0;
  21. this._controlTarget={x:0.0001,y:0,z:0};
  22. this.resType={"video":"video","box":"box","slice":"slice"};
  23. this.asteroidConfig={enable:false,assteroidFPS:36,assteroidFov:135,asteroidForwardTime:2000,asteroidWaitTime:1000,asteroidDepressionRate:0.5,asteroidTop:1,cubeResolution:2048};
  24. this.VRhint="请取消屏幕翻转锁定后装入VR盒子中";
  25. this.camera=new THREE.PerspectiveCamera(this.cameraPara.fov,this.cameraPara.aspect , this.cameraPara.near, this.cameraPara.far);
  26. //this.camera=new THREE.OrthographicCamera(window.innerWidth / -16, window.innerWidth / 16, window.innerHeight / 16, window.innerHeight / -16, -200, 500);
  27. this.camera.position.set(this.cameraPosition.x, this.cameraPosition.y, this.cameraPosition.z);
  28. this.loadProgressManager=new THREE.LoadingManager(function (xhr){
  29. console.log("loaded");
  30. },function(item, loaded, total){
  31. console.log("item=",item,"loaded",loaded,"total=",total);
  32. },function (xhr,cl) {
  33. console.log(xhr,cl);
  34. });
  35. this.scene.add(this.camera);
  36. this.scene.add(this.VRObject);
  37. //this.renderer.setPixelRatio( window.devicePixelRatio );
  38. this.effect = AVR.stereoEffect(this.renderer);
  39. }
  40. VR.prototype.init=function () {
  41. var that=this;
  42. function render() {
  43. var width = that.container.offsetWidth;
  44. var height = that.container.offsetHeight;
  45. that.camera.aspect = width / height;
  46. if((AVR.isMobileDevice() && AVR.isCrossScreen())) {
  47. that.effect.setSize(width, height);
  48. that.effect.render(that.scene, that.camera);
  49. }else{
  50. that.renderer.setSize(width, height);
  51. that.renderer.setClearColor(new THREE.Color(0xffffff));
  52. that.renderer.render(that.scene, that.camera);
  53. }
  54. that.camera.updateProjectionMatrix();
  55. if(that.controls){
  56. that.controls.update(that.clock.getDelta());
  57. }
  58. }
  59. function animate() {
  60. requestAnimationFrame(animate);
  61. render();
  62. }
  63. animate();
  64. //if you don't use an asteroid view,you need to initialize the controller after the asteroid view.
  65. if(!that.asteroidConfig.enable){
  66. AVR.bindOrientationEnevt(that,that._controlTarget);
  67. }
  68. window.addEventListener('resize', function () {
  69. AVR.bindOrientationEnevt(that,that._controlTarget);
  70. }, false);
  71. };
  72. VR.prototype.playPanorama=function (recUrl,resType,objName) {
  73. objName=objName||"__panoContainer";
  74. var that = this;
  75. that._containerRadius=(that.resType.box == resType|| that.resType.slice==resType) ? (that.vrbox.width / 2.1) : that.vrbox.radius;
  76. that.autoHideLeftTime = that.defaultAutoHideLeftTime;
  77. that.voiceHideLeftTime = that.defaultVoiceHideLeftTime;
  78. that.videoToolBar = AVR.videoToolBar(that.container);
  79. that.container.addEventListener("click", function () {
  80. that.autoHideLeftTime = that.defaultAutoHideLeftTime;
  81. that.videoToolBar.toolbar.style.display = "block";
  82. });
  83. that.videoToolBar.gyroBtn.addEventListener("click", function () {
  84. if (!this.getAttribute("active")) {
  85. that.controls.gyroFreeze();
  86. btnActive(this);
  87. btnActive(that.videoToolBar.circle1);
  88. btnActive(that.videoToolBar.circle2);
  89. this.setAttribute("active", "active");
  90. } else {
  91. that.controls.gyroUnfreeze();
  92. this.removeAttribute("active");
  93. btnInactive(this);
  94. btnInactive(that.videoToolBar.circle1);
  95. btnInactive(that.videoToolBar.circle2);
  96. }
  97. }, false);
  98. that.videoToolBar.vrBtn.addEventListener("click", function (e) {
  99. if (AVR.isMobileDevice()) {
  100. if (AVR.OS.isWeixin() && !AVR.OS.isiOS()) {
  101. if (that.video.getAttribute('x5-video-orientation') == "landscape") {
  102. that.video.setAttribute('x5-video-orientation', 'portraint');
  103. btnInactive(this);
  104. } else {
  105. that.video.setAttribute('x5-video-orientation', 'landscape');
  106. btnActive(this);
  107. }
  108. } else {
  109. if (!AVR.isCrossScreen()) {
  110. btnInactive(this);
  111. AVR.msgBox(that.VRhint, 5, that.container);
  112. } else {
  113. btnActive(this);
  114. AVR.fullscreen(that.container);
  115. }
  116. }
  117. } else {
  118. if (!this.getAttribute("fullscreen")) {
  119. btnActive(this);
  120. this.setAttribute("fullscreen", "true");
  121. } else {
  122. btnInactive(this);
  123. this.removeAttribute("fullscreen");
  124. }
  125. AVR.fullscreen(that.container);
  126. }
  127. }, false);
  128. that.renderer.domElement.addEventListener( 'wheel', function(e) {
  129. var delta = e.deltaY > 0 ? 15 : -15;
  130. if (that.camera.fov + delta * 0.05 >= 10 && that.camera.fov + delta * 0.05 <= 120) {
  131. fovChange(delta);
  132. }
  133. }, false );
  134. function fovChange(delta){
  135. that.camera.fov += delta * 0.05;
  136. that.camera.updateProjectionMatrix();
  137. }
  138. window.addEventListener('resize', function () {
  139. if (!AVR.isFullscreen()) {
  140. if (AVR.OS.isWeixin() && !AVR.OS.isiOS()) {
  141. if (that.video.getAttribute('x5-video-orientation') == "landscape") {
  142. btnActive(that.videoToolBar.vrBtn);
  143. } else {
  144. btnInactive(that.videoToolBar.vrBtn);
  145. }
  146. AVR.isCrossScreen(function (ret) {
  147. if (ret) {
  148. btnActive(that.videoToolBar.vrBtn);
  149. } else {
  150. btnInactive(that.videoToolBar.vrBtn);
  151. }
  152. });
  153. } else {
  154. AVR.isCrossScreen(function (ret) {
  155. if (ret) {
  156. btnActive(that.videoToolBar.vrBtn);
  157. } else {
  158. btnInactive(that.videoToolBar.vrBtn);
  159. }
  160. });
  161. btnInactive(that.videoToolBar.vrBtn);
  162. }
  163. } else {
  164. if (AVR.isMobileDevice()) {
  165. AVR.isCrossScreen(function (ret) {
  166. if (ret) {
  167. btnActive(that.videoToolBar.vrBtn);
  168. } else {
  169. btnInactive(that.videoToolBar.vrBtn);
  170. }
  171. });
  172. } else {
  173. btnActive(that.videoToolBar.vrBtn);
  174. }
  175. }
  176. bindVolumeEvent();
  177. }, false);
  178. function btnActive(obj) {
  179. obj.style.borderColor = "green";
  180. obj.style.color = "green";
  181. }
  182. function btnInactive(obj) {
  183. obj.style.borderColor = "white";
  184. obj.style.color = "white";
  185. }
  186. that._play=function() {
  187. that.videoToolBar.btn.style.border = "none";
  188. that.videoToolBar.btn.style.fontWeight = 800;
  189. that.videoToolBar.btn.innerHTML = "<b>||</b>";
  190. }
  191. that._pause=function() {
  192. that.videoToolBar.btn.innerText = "";
  193. that.videoToolBar.btn.style.borderTop = "0.6rem solid transparent";
  194. that.videoToolBar.btn.style.borderLeft = "1rem solid white";
  195. that.videoToolBar.btn.style.borderBottom = "0.6rem solid transparent";
  196. }
  197. if (that.resType.box == resType) {
  198. var textures = [];
  199. var materials = [];
  200. var imageObj = new Image();
  201. imageObj.src = recUrl;
  202. imageObj.onload = function () {
  203. var canvas, context;
  204. var tileWidth = imageObj.height;
  205. for (var i = 0; i < 6; i++) {
  206. textures[i] = new THREE.Texture();
  207. canvas = document.createElement('canvas');
  208. context = canvas.getContext('2d');
  209. canvas.height = tileWidth;
  210. canvas.width = tileWidth;
  211. context.drawImage(imageObj, tileWidth * i, 0, tileWidth, tileWidth, 0, 0, tileWidth, tileWidth);
  212. textures[i].image = canvas;
  213. textures[i].needsUpdate = true;
  214. materials.push(new THREE.MeshBasicMaterial({map: textures[i]}));
  215. }
  216. var Box = new THREE.Mesh(new THREE.CubeGeometry(that.vrbox.width, that.vrbox.height, that.vrbox.depth), new THREE.MultiMaterial(materials));
  217. Box.applyMatrix(new THREE.Matrix4().makeScale(1, 1, -1));
  218. Box.visible=false;
  219. Box.name = objName;
  220. that.VRObject.add(Box);
  221. that.loadProgressManager.onLoad();
  222. }
  223. imgPanoToolBar();
  224. }else if(that.resType.slice==resType){
  225. var cubeGeometry = new THREE.CubeGeometry(that.vrbox.width, that.vrbox.height, that.vrbox.depth, that.sliceSegment, that.sliceSegment, that.sliceSegment);
  226. //cubeGeometry.scale(-1, 1, 1)
  227. var textureLoader = new THREE.TextureLoader(that.loadProgressManager);
  228. textureLoader.mapping = THREE.UVMapping;
  229. var materials=[];
  230. for(var i=0;i<recUrl.length;i++){
  231. materials.push(new THREE.MeshBasicMaterial({map:textureLoader.load(recUrl[i])}));
  232. }
  233. var faceId=0;
  234. var uv = [new THREE.Vector2(0, 0), new THREE.Vector2(1, 0), new THREE.Vector2(1, 1), new THREE.Vector2(0, 1)];
  235. for (var i = 0, l = cubeGeometry.faces.length; i < l; i+=2) {
  236. cubeGeometry.faces[i].materialIndex = faceId;
  237. cubeGeometry.faces[i + 1].materialIndex = faceId;
  238. cubeGeometry.faceVertexUvs[0][i] = [uv[3], uv[0], uv[2]];
  239. cubeGeometry.faceVertexUvs[0][i + 1] = [uv[0], uv[1], uv[2]];
  240. faceId++;
  241. }
  242. var cube=new THREE.Mesh(cubeGeometry,materials);
  243. cube.applyMatrix(new THREE.Matrix4().makeScale(1, 1, -1));
  244. cube.name = objName;
  245. cube.visible=false;
  246. that.VRObject.add(cube);
  247. if(that.asteroidConfig.enable){
  248. that.cubeCamera = new THREE.CubeCamera(that._containerRadius, that.cameraPara.far, that.asteroidConfig.cubeResolution);
  249. var cubeCameraTexture=that.cubeCamera.renderTarget.texture;
  250. cubeCameraTexture.minFilter = THREE.LinearMipMapLinearFilter;
  251. that.VRObject.add(that.cubeCamera);
  252. material = new THREE.MeshBasicMaterial({
  253. envMap: that.cubeCamera.renderTarget.texture, side: THREE.BackSide
  254. });
  255. sphere = new THREE.Mesh(new THREE.SphereGeometry(that._containerRadius, 180, 180), material);
  256. sphere.position.set(0,0,0);
  257. that.VRObject.add(sphere);
  258. that.asteroidForward=function(callback){
  259. that.cubeCamera.update(that.renderer, that.scene);
  260. asteroidForward(callback);
  261. }
  262. }else {
  263. that._controlTarget = {x: -0.001, y: 0, z: 0};
  264. }
  265. imgPanoToolBar();
  266. } else {
  267. var phiStart = AVR.isMobileDevice()?Math.PI/2:-Math.PI/2;
  268. var geometry = geometry = new THREE.SphereBufferGeometry(this.vrbox.radius, this.vrbox.widthSegments, this.vrbox.heightSegments, phiStart);
  269. geometry.scale(-1, 1, 1); //x取反(面朝里)
  270. if (that.resType.video == resType) {
  271. var video = AVR.createTag('video', {
  272. 'webkit-playsinline': true,
  273. 'playsinline': true,
  274. 'preload': 'auto',
  275. 'x-webkit-airplay': 'allow',
  276. 'x5-playsinline': true,
  277. 'x5-video-player-type': 'h5',
  278. 'x5-video-player-fullscreen': true,
  279. 'x5-video-orientation': 'portrait',
  280. 'width': AVR.Broswer.isIE() ? 1920 : 2048,
  281. 'height': AVR.Broswer.isIE() ? 960 : 1024,
  282. 'src': recUrl,
  283. 'style': 'object-fit: fill'
  284. }, {
  285. 'allowsInlineMediaPlayback': true
  286. });
  287. var buffTimer = false;
  288. setInterval(function (e) {
  289. that.videoToolBar.playProgress.style.width = (video.currentTime / video.duration) * 100 + "%";
  290. that.videoToolBar.curTime.innerText = AVR.formatSeconds(video.currentTime);
  291. that.videoToolBar.totalTime.innerText = AVR.formatSeconds(video.duration);
  292. if (that.autoHideLeftTime < 0 && !video.paused) {
  293. that.videoToolBar.toolbar.style.display = "none";
  294. } else {
  295. that.autoHideLeftTime--;
  296. }
  297. }, 1000);
  298. video.addEventListener("canplaythrough", function (e) {
  299. if (!buffTimer) {
  300. buffTimer = setInterval(function (e) {
  301. var allBuffered = 0;
  302. if (video.buffered.length != 0) {
  303. allBuffered += video.buffered.end(0);
  304. }
  305. if (allBuffered >= video.duration) {
  306. clearInterval(buffTimer);
  307. }
  308. that.videoToolBar.loadedProgress.style.width = (allBuffered / video.duration) * 100 + "%";
  309. }, 500);
  310. }
  311. }, false);
  312. that.videoToolBar.progressBar.addEventListener("click", function (e) {
  313. video.currentTime = video.duration * (e.clientX / this.clientWidth);
  314. }, false);
  315. that.videoToolBar.btn.addEventListener("click", function (e) {
  316. video.paused ? (function () {
  317. that._play();
  318. video.play();
  319. })() : (function () {
  320. that._pause();
  321. video.pause();
  322. })();
  323. });
  324. that.video = video;
  325. var eventTester = function(e){
  326. that.video.addEventListener(e,function(){
  327. that.loadProgressManager.onLoad();
  328. },false);
  329. }
  330. eventTester("canplay");
  331. var texture = new THREE.VideoTexture(video);
  332. texture.generateMipmaps = false;
  333. texture.minFilter = THREE.LinearFilter;
  334. texture.magFilter = THREE.LinearFilter;
  335. //texture.format = THREE.RGBFormat;
  336. texture.format = THREE.RGBAFormat;
  337. that.VRObject.add(buildTexture(texture));
  338. }else {
  339. imgPanoToolBar();
  340. new THREE.TextureLoader(that.loadProgressManager).load(recUrl, function (texture) {
  341. that.VRObject.add(buildTexture(texture));
  342. });
  343. }
  344. function buildTexture(texture) {
  345. material = new THREE.MeshBasicMaterial({overdraw: true, map: texture});
  346. mesh = new THREE.Mesh(geometry, material);
  347. mesh.visible=false;
  348. mesh.name = objName;
  349. //mesh.material.map=;
  350. //that.VRObject.add(mesh);
  351. if(that.asteroidConfig.enable){
  352. that.asteroidForward=function(callback){
  353. asteroidForward(callback);
  354. }
  355. }
  356. return mesh;
  357. //that.renderer.setPixelRatio(window.devicePixelRatio);
  358. }
  359. }
  360. function asteroidForward(callback) {
  361. var config = that.asteroidConfig;
  362. var defaultFov = that.camera.fov;
  363. var s = that._containerRadius;
  364. that.camera.position.y = s * config.asteroidTop;
  365. that.camera.fov = config.assteroidFov;
  366. //that.camera.lookAt(0,0,0);
  367. var v = s / config.asteroidForwardTime * config.assteroidFPS;
  368. var sFov = that.camera.fov - defaultFov;
  369. var vFov = sFov / config.asteroidForwardTime * config.assteroidFPS;
  370. var vRo = Math.PI / 2 / config.asteroidForwardTime * config.assteroidFPS;
  371. //console.log("s=",s,"speed v=",v,"sFov=",sFov,"speed sfov=",vFov,"vRo=",vRo)
  372. that.camera.lookAt(0, 0, AVR.isMobileDevice() ? -0.001 : 0.001);
  373. setTimeout(function () {
  374. var asteroidForwardTimer = setInterval(function () {
  375. //console.log("new y=",config.asteroidTop*that.camera.position.y -v);
  376. if (config.asteroidTop * that.camera.position.y - v <= 0) {
  377. that.camera.position.y = 0;
  378. that.camera.fov = defaultFov;
  379. clearInterval(asteroidForwardTimer);
  380. that._controlTarget={x:0.0001,y:0,z:0};
  381. AVR.bindOrientationEnevt(that,that._controlTarget);
  382. if(void 0 !== callback){
  383. // Wait for the controller to initialize to complete the callback.
  384. var controlTimer=setInterval(function () {
  385. if(that.controls){
  386. clearInterval(controlTimer);
  387. callback();
  388. }
  389. })
  390. }
  391. } else {
  392. that.camera.position.y -= (v * config.asteroidTop);
  393. that.camera.fov -= vFov;
  394. that.camera.rotateOnAxis(new THREE.Vector3(1, 0, 0), vRo * config.asteroidTop);
  395. }
  396. that.camera.updateProjectionMatrix();
  397. }, config.assteroidFPS);
  398. }, config.asteroidWaitTime);
  399. }
  400. function imgPanoToolBar() {
  401. setInterval(function (e) {
  402. if (that.autoHideLeftTime < 0) {
  403. that.videoToolBar.toolbar.style.display = "none";
  404. } else {
  405. that.autoHideLeftTime--;
  406. }
  407. }, 1000);
  408. that.videoToolBar.btn.addEventListener("click", function (e) {
  409. that.controls.autoRotate ? that._pause() : that._play();
  410. that.controls.autoRotate = !that.controls.autoRotate;
  411. });
  412. }
  413. function bindVolumeEvent() {
  414. var Audio=that.video || that.audio
  415. if (Audio) {
  416. that.videoToolBar.voice_bar.style.display = "block";
  417. var voice_bar = that.videoToolBar.voice_bar;
  418. var voice_slide_bar = voice_bar.firstChild;
  419. var voice_cur_slide_bar = voice_slide_bar.firstChild;
  420. var voice_btn = voice_cur_slide_bar.firstChild;
  421. var mouseDown = false, touchStartY = 0, touchCurrentY = 0, tempY;
  422. Audio.volume = that.defaultVolume;
  423. voice_cur_slide_bar.style.height = (Audio.volume * that.container.clientHeight) + "px";
  424. voice_bar.addEventListener("mousedown", function (e) {
  425. voice_bar.style.opacity = 1;
  426. }, false);
  427. voice_slide_bar.addEventListener("click", function (e) {
  428. e.preventDefault();
  429. that.voiceHideLeftTime = that.defaultVoiceHideLeftTime;
  430. voice_cur_slide_bar.style.height = this.clientHeight - e.clientY + "px";
  431. Audio.volume = (this.clientHeight - e.clientY) / this.clientHeight;
  432. }, false);
  433. voice_btn.addEventListener("mousedown", function (e) {
  434. mouseDown = true;
  435. }, false);
  436. voice_btn.addEventListener("mouseup", function (e) {
  437. mouseDown = false;
  438. }, false);
  439. voice_bar.addEventListener("mousemove", function (e) {
  440. that.voiceHideLeftTime = that.defaultVoiceHideLeftTime;
  441. if (mouseDown) {
  442. voice_cur_slide_bar.style.height = this.clientHeight - e.clientY + "px";
  443. if ((this.clientHeight - e.clientY) / this.clientHeight <= 1)
  444. Audio.volume = (this.clientHeight - e.clientY) / this.clientHeight;
  445. }
  446. }, false);
  447. voice_bar.addEventListener("touchstart", function (e) {
  448. e.preventDefault();
  449. that.voiceHideLeftTime = that.defaultVoiceHideLeftTime;
  450. tempY = voice_cur_slide_bar.clientHeight;
  451. touchStartY = e.touches[0].pageY;
  452. voice_bar.style.opacity = 1;
  453. }, false);
  454. voice_bar.addEventListener("touchmove", function (e) {
  455. e.preventDefault();
  456. that.voiceHideLeftTime = that.defaultVoiceHideLeftTime;
  457. touchCurrentY = e.touches[0].pageY;
  458. voice_cur_slide_bar.style.height = tempY + (touchStartY - touchCurrentY) + "px";
  459. if (voice_cur_slide_bar.clientHeight / this.clientHeight <= 1)
  460. Audio.volume = voice_cur_slide_bar.clientHeight / this.clientHeight;
  461. }, false);
  462. voice_bar.addEventListener("touchend", function (e) {
  463. tempY = 0;
  464. }, false);
  465. setInterval(function () {
  466. if (that.voiceHideLeftTime <= 0) {
  467. voice_bar.style.opacity = 0;
  468. } else {
  469. that.voiceHideLeftTime--;
  470. }
  471. }, 1000);
  472. }
  473. }
  474. bindVolumeEvent();
  475. };
  476. VR.prototype.sphere2BoxPano=function(img,w,h,callback) {
  477. var that = this;
  478. var fases = { 'x': 'x','nx': 'nx', 'ny': 'ny','y': 'y', 'z': 'z', 'nz': 'nz'};
  479. var canvasArr=[],finishNum=0;
  480. var i=0;
  481. var image = new Image();
  482. image.crossOrigin="anonymous";
  483. image.src = img;
  484. image.onload = function() {
  485. for (var id in fases) {
  486. var canvas = document.createElement('canvas');
  487. canvas.width = w;
  488. canvas.height = h;
  489. canvas.id = "face_" + id;
  490. canvasArr[i] = canvas;
  491. var gl = canvas.getContext('webgl', {preserveDrawingBuffer: true});//获取canvas上下文
  492. var shaderPorgram = initShaders(gl, id);//初始化着色器程序
  493. var num = initVertexBuffers(gl, shaderPorgram);
  494. var PI = gl.getUniformLocation(shaderPorgram, 'PI');
  495. gl.uniform1f(PI, Math.PI);
  496. gl.clearColor(0.0, 0.0, 0.0, 1.0);
  497. // Set texture
  498. if (!initTextures(gl, shaderPorgram, num,image)) {
  499. console.log('Failed to intialize the texture.');
  500. //return;
  501. }
  502. i++;
  503. }
  504. }
  505. //初始化纹理
  506. function initTextures(gl,shaderPorgram,n,image){
  507. var texture = gl.createTexture();//创建纹理对象
  508. if(!texture){
  509. console.log('Failed to create the texture object!');
  510. return false;
  511. }
  512. var u_Sampler = gl.getUniformLocation(shaderPorgram,'u_Sampler');
  513. loadTextures(gl,n,texture,u_Sampler,image);
  514. return true;
  515. }
  516. //加载纹理图片
  517. function loadTextures(gl,n,texture,u_Sampler,image){
  518. if(that.asteroidConfig.enable) {
  519. gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, -1);//对纹理图像进行y轴反转
  520. }
  521. gl.activeTexture(gl.TEXTURE0);//激活纹理单元
  522. gl.bindTexture(gl.TEXTURE_2D, texture);//绑定纹理对象
  523. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);//配置纹理对象的参数
  524. /**
  525. * RENDER WARNING: texture bound to texture unit 0 is not renderable. It maybe non-power-of-2 and have incompatible texture filtering.
  526. * 大致意思是纹理没有渲染成功,因为所使用的图片的分辨率不是2的幂数,2的幂数是指2*2、4*4、8*8、16*16...256*256...;
  527. * 需要设置图形纹理参数时设置水平垂直拉伸。
  528. */
  529. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  530. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  531. gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);//将纹理图像分配给纹理对象
  532. gl.uniform1i(u_Sampler, 0);//将0号纹理传给着色器中的取样器变量
  533. //gl.clear(gl.COLOR_BUFFER_BIT); // Clear <canvas>
  534. gl.drawArrays(gl.TRIANGLE_STRIP, 0, n); // Draw the rectangle
  535. if(finishNum<5){
  536. finishNum++;
  537. }else{
  538. callback(getNewPano());
  539. }
  540. //gl.drawArrays(gl.TRIANGLE_STRIP, 1, n); // Draw the rectangle
  541. }
  542. //初始化顶点位置
  543. function initVertexBuffers(gl,shaderPorgram){
  544. //顶点坐标和纹理坐标映射关系
  545. var datas = new Float32Array([
  546. //顶点坐标、纹理坐标
  547. -1.0,1.0,0.0,1.0,
  548. -1.0,-1.0,0.0,0.0,
  549. 1.0,1.0,1.0,1.0,
  550. 1.0,-1.0,1.0,0.0,
  551. ]);
  552. var num = 4;//顶点数目
  553. var vertexBuffer = gl.createBuffer();//创建缓冲区对象
  554. if(!vertexBuffer){
  555. console.log('Failed to create the buffer object!');
  556. return -1;
  557. }
  558. //将缓冲区对象绑定到目标并写入数据
  559. gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer);
  560. gl.bufferData(gl.ARRAY_BUFFER,datas,gl.STATIC_DRAW);
  561. var size = datas.BYTES_PER_ELEMENT;//数组中的每个元素的大小(以字节为单位)
  562. //顶点着色器接受顶点坐标和纹理坐标映射关系
  563. var a_Position = gl.getAttribLocation(shaderPorgram,'a_Position');
  564. gl.vertexAttribPointer(a_Position,2,gl.FLOAT,false,size*4,0);
  565. gl.enableVertexAttribArray(a_Position);
  566. var a_TexCoord = gl.getAttribLocation(shaderPorgram,'a_TexCoord');
  567. gl.vertexAttribPointer(a_TexCoord,2,gl.FLOAT,false,size*4,size*2);
  568. gl.enableVertexAttribArray(a_TexCoord);
  569. return num;
  570. }
  571. //初始化着色器
  572. function initShaders(gl, type) {
  573. var shaderProgram;
  574. var fragmentShader = getShader(gl, type);
  575. var vertexShader = getShader(gl);
  576. //创建着色器
  577. shaderProgram = gl.createProgram();
  578. gl.attachShader(shaderProgram, vertexShader);
  579. gl.attachShader(shaderProgram, fragmentShader);
  580. //链接着色器程序
  581. gl.linkProgram(shaderProgram);
  582. //检查着色器是否成功链接
  583. if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
  584. return null;
  585. }
  586. //链接成功后激活渲染器程序
  587. gl.useProgram(shaderProgram);
  588. //启用顶点缓冲区数组
  589. //gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
  590. //shaderProgram.vertexColorAttribute = gl.getAttribLocation(shaderProgram, "aVertexColor");
  591. //gl.enableVertexAttribArray(shaderProgram.vertexColorAttribute);
  592. //shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
  593. //shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
  594. return shaderProgram;
  595. }
  596. //根据id获得编译好的着色器
  597. function getShader(gl, id) {
  598. var shaderScript;
  599. var shader;
  600. //创建顶点着色器或片段着色器
  601. if (id) {
  602. shaderScript = getShaderFragment(id);
  603. if (!shaderScript) {
  604. return null;
  605. }
  606. shader = gl.createShader(gl.FRAGMENT_SHADER);
  607. } else {
  608. shaderScript = getShaderVertex()
  609. shader = gl.createShader(gl.VERTEX_SHADER);
  610. }
  611. gl.shaderSource(shader, shaderScript);
  612. //编译着色器代码
  613. gl.compileShader(shader);
  614. //检查是否编译成功
  615. if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
  616. console.log(gl.getShaderInfoLog(shader));//打印编译失败信息
  617. return null;
  618. }
  619. //成功编译返回编译好的着色器
  620. return shader;
  621. }
  622. //<!-- 片段着色器程序 -->
  623. function getShaderFragment(type) {
  624. var code = "";
  625. var variable = "\n\
  626. precision mediump float;\n\
  627. varying vec2 v_TexCoord;\n\
  628. uniform sampler2D u_Sampler;\n\
  629. uniform float PI;\n";
  630. //超出范围处理\
  631. var checkRange = "\n\
  632. if(abs(theta)>PI){\n\
  633. if(theta>PI){\n\
  634. theta -= 2.0*PI;\n\
  635. }else{\n\
  636. theta += 2.0*PI;\n\
  637. }\n\
  638. }\n\
  639. if(abs(phi)>PI/2.0){\n\
  640. if(phi>PI/2.0){\n\
  641. phi -= PI;\n\
  642. }else{\
  643. phi += PI;\n\
  644. }\n\
  645. }\n\
  646. float x = theta/PI*0.5 + 0.5;\n\
  647. float y = phi/PI*2.0*0.5 + 0.5;\n\
  648. gl_FragColor = texture2D(u_Sampler, vec2(x,y));\n";
  649. if (type == 'z') {
  650. //z轴正平面-z\
  651. code = variable + "\n\
  652. void main() {\n\
  653. float r = 0.5;\n\
  654. vec2 orig = vec2(0.5-v_TexCoord.x,v_TexCoord.y-0.5);\n\
  655. float theta = atan(orig.x,r);\n\
  656. float phi = atan(orig.y*cos(theta),r);" + checkRange + "\n\
  657. }\n";
  658. } else if (type == "nz") {
  659. //z轴负平面-nz\
  660. code = variable + "\n\
  661. void main() {\n\
  662. float r = 0.5;\n\
  663. vec2 orig = vec2(0.5-v_TexCoord.x,v_TexCoord.y-0.5);\n\
  664. float theta = atan(orig.x,r);\n\
  665. float phi = atan(orig.y*cos(theta),r);\n\
  666. theta = theta+PI;\n" + checkRange + "\n\
  667. }\n";
  668. } else if (type == "x") {
  669. //x轴正平面-x\
  670. code = variable + "\n\
  671. void main() {\n\
  672. float r = 0.5;\n\
  673. vec2 orig = vec2(v_TexCoord.x-0.5,v_TexCoord.y-0.5);\n\
  674. float theta = atan(r,orig.x);\n\
  675. float phi = atan(orig.y*sin(theta),r);" + checkRange + "\n\
  676. }\n";
  677. } else if (type == "nx") {
  678. //x轴负平面-nx\
  679. code = variable + "\n\
  680. void main() {\n\
  681. float r = 0.5;\n\
  682. vec2 orig = vec2(v_TexCoord.x-0.5,v_TexCoord.y-0.5);\n\
  683. float theta = atan(r,orig.x);\n\
  684. float phi = atan(orig.y*sin(theta),r);\n\
  685. theta = theta+PI;" + checkRange + "\n\
  686. }\n";
  687. } else if (type == "y") {
  688. //y轴正平面-y\
  689. code = variable + "\n\
  690. void main() {\n\
  691. float r = 0.5;\n\
  692. vec2 orig = vec2(0.5-v_TexCoord.x,0.5-v_TexCoord.y);\n\
  693. float theta = atan(orig.x,orig.y);\n\
  694. float phi = atan(r*sin(theta),orig.x);" + checkRange + "\n\
  695. }\n";
  696. } else if (type == "ny") {
  697. //y轴负平面-ny
  698. code = variable + "\n\
  699. void main() {\n\
  700. float r = 0.5;\n\
  701. vec2 orig = vec2(0.5-v_TexCoord.x,v_TexCoord.y-0.5);\n\
  702. float theta = atan(orig.x,orig.y);\n\
  703. float phi = atan(r*sin(theta),orig.x);\n\
  704. phi = -phi;" + checkRange + "\n\
  705. }\n";
  706. } else {
  707. console.error("shader fragment type error!");
  708. }
  709. return code;
  710. }
  711. function getShaderVertex() {
  712. var code = "\n\
  713. attribute vec4 a_Position;\n\
  714. attribute vec2 a_TexCoord;\n\
  715. varying vec2 v_TexCoord;\n\
  716. void main() {\n\
  717. gl_Position= a_Position;\n\
  718. v_TexCoord = a_TexCoord;\n\
  719. }\n";
  720. return code;
  721. }
  722. function getNewPano() {
  723. var c = document.createElement('canvas'),ctx = c.getContext('2d');
  724. c.width = w * 6;
  725. c.height = h;
  726. var tmp=document.createElement('canvas'),tmpctx = tmp.getContext('2d');
  727. tmp.width = w;
  728. tmp.height=h;
  729. var degree = 180 * Math.PI / 180;
  730. tmpctx.rotate(degree);
  731. if(that.sliceSegment) {
  732. var sliceArray=[];
  733. var canvasCell=document.createElement("canvas");
  734. canvasCell.width=h/that.sliceSegment;
  735. canvasCell.height=h/that.sliceSegment;
  736. var canvasCtx=canvasCell.getContext("2d");
  737. for (var idx in canvasArr) {
  738. tmpctx.drawImage(canvasArr[idx], 0, 0, -w, -h);
  739. for(var row=0;row<that.sliceSegment;row++){
  740. for(var col=0;col<that.sliceSegment;col++){
  741. canvasCtx.putImageData(tmpctx.getImageData(col*(h/that.sliceSegment),row*(h/that.sliceSegment),h*(col+1)/that.sliceSegment,h*(row+1)/that.sliceSegment),0,0);
  742. sliceArray.push(canvasCell.toDataURL());
  743. }
  744. }
  745. }
  746. return sliceArray;
  747. }else{
  748. for (var idx in canvasArr) {
  749. tmpctx.drawImage(canvasArr[idx], 0, 0, -w, -h);
  750. ctx.drawImage(tmp, w * idx, 0, w, h);
  751. }
  752. return c.toDataURL();
  753. }
  754. };
  755. }
  756. var AR=function (scene,renderer,container,cameraPara,cameraPosition) {
  757. var that = this;
  758. AVR.setCameraPara(that,cameraPara,cameraPosition);
  759. this.scene = scene;
  760. this.renderer = renderer;
  761. this.container = container;
  762. this.constraints = {};
  763. this.video = null;
  764. this.openAudio = false;
  765. this.cameraIndex = 1;//0为前置摄像头,否则为后置
  766. this._controlTarget={x:0.0001,y:0,z:0};
  767. this.camera=new THREE.PerspectiveCamera(this.cameraPara.fov,this.cameraPara.aspect , this.cameraPara.near, this.cameraPara.far);
  768. this.camera.position.set(this.cameraPosition.x, this.cameraPosition.y, this.cameraPosition.z);
  769. this.cameraReady=false;
  770. this.scene.add(this.camera);
  771. this.clock = new THREE.Clock();
  772. this.tempCanvas=document.createElement('canvas');
  773. this.effect = AVR.stereoEffect(this.renderer);
  774. this._takeScreenShot=false;
  775. }
  776. AR.prototype.init=function () {
  777. var self = this;
  778. AVR.bindOrientationEnevt(self, self._controlTarget);
  779. this.video = AVR.createTag('video', {
  780. 'webkit-playsinline': true,
  781. 'playsinline': true,
  782. 'preload': 'auto',
  783. 'x-webkit-airplay': 'allow',
  784. 'x5-playsinline': true,
  785. 'x5-video-player-type': 'h5',
  786. 'x5-video-player-fullscreen': true,
  787. 'x5-video-orientation': 'portrait',
  788. 'style': 'object-fit: fill',
  789. 'autoplay':"autoplay"
  790. }, {
  791. 'allowsInlineMediaPlayback': true
  792. });
  793. //this.video.style.display = "none";
  794. this.video.style.zIndex="-99999";
  795. this.video.style.position="absolute";
  796. this.video.style.left="0px";
  797. this.video.style.top="0px";
  798. this.video.style.width="2px";
  799. this.video.style.height="2px";
  800. document.body.appendChild(this.video);
  801. this.video.oncanplaythrough = function () {
  802. self.cameraReady = true;
  803. if (self.video.readyState === self.video.HAVE_ENOUGH_DATA) {
  804. self.cameraTexture = new THREE.VideoTexture(self.video);
  805. self.cameraTexture.generateMipmaps = false;
  806. self.cameraTexture.format = THREE.RGBAFormat;
  807. self.cameraTexture.maxFilter = THREE.NearestFilter;
  808. self.cameraTexture.minFilter = THREE.NearestFilter;
  809. self.scene.background = self.cameraTexture; // 背景视频纹理
  810. self.cameraTexture.needsUpdate = true;
  811. }
  812. }
  813. navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
  814. window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL;
  815. if (navigator.getUserMedia) {
  816. var medias = {
  817. audio: self.openAudio,
  818. video: {
  819. facingMode: {
  820. exact: self.cameraIndex?"environment":"user"
  821. }
  822. }
  823. }
  824. navigator.getUserMedia(medias, successCallback, errorCallback);
  825. } else {
  826. alert('Native device meadia streaming(getUserMdeia) not supported in this browser.')
  827. }
  828. function successCallback(stream) {
  829. self.video.srcObject = stream;
  830. };
  831. function errorCallback(err) {
  832. alert(err);
  833. };
  834. }
  835. AR.prototype.takeCameraPhoto=function() {
  836. var ctx = this.tempCanvas.getContext('2d');
  837. ctx.clearRect(0,0,window.innerWidth,window.innerHeight);
  838. ctx.drawImage(this.video, 0, 0,window.innerWidth,window.innerHeight); //将video对象内指定的区域捕捉绘制到画布上指定的区域,实现拍照。
  839. return ctx.toDataURL();
  840. }
  841. AR.prototype.takeScreenShot=function (callback) {
  842. this._takeScreenShot=true;
  843. this._takeScreenShotCallback=callback;
  844. }
  845. AR.prototype.play=function () {
  846. var that=this;
  847. function render() {
  848. if(that._takeScreenShot){
  849. that._takeScreenShot=false;
  850. var screenshot=that.renderer.domElement.toDataURL();
  851. that._takeScreenShotCallback(screenshot);
  852. }
  853. if(that.cameraReady) {
  854. var width = window.innerWidth;
  855. var height = window.innerHeight;
  856. that.camera.aspect = width / height;
  857. that.cameraTexture.repeat.y = height / that.video.videoHeight;
  858. that.cameraTexture.offset.x = 0;
  859. that.cameraTexture.offset.y = 0;
  860. if ((AVR.isMobileDevice() && AVR.isCrossScreen())) {
  861. that.cameraTexture.repeat.x = width / (2 * that.video.videoWidth);
  862. that.effect.setSize(width, height);
  863. that.effect.render(that.scene, that.camera);
  864. } else {
  865. that.cameraTexture.repeat.x = width / that.video.videoWidth;
  866. that.renderer.setSize(width, height);
  867. that.renderer.setClearColor(new THREE.Color(0xffffff));
  868. that.renderer.render(that.scene, that.camera);
  869. }
  870. that.camera.updateProjectionMatrix();
  871. }
  872. if (that.controls) {
  873. that.controls.update(that.clock.getDelta());
  874. }
  875. }
  876. function animate() {
  877. requestAnimationFrame(animate);
  878. render();
  879. }
  880. animate();
  881. }
  882. var AVR= {
  883. debug: false,
  884. startGyro:function () {
  885. var self=this;
  886. window.addEventListener("deviceorientation", orientationHandler, false);
  887. function orientationHandler(event) {
  888. /*"左右旋转:rotate alpha{" + Math.round(event.alpha) + "deg)";
  889. "前后旋转:rotate beta{" + Math.round(event.beta) + "deg)";
  890. "扭转设备:rotate gamma{" + Math.round(event.gamma) + "deg);*/
  891. //AVR.msgBox(Math.sin(Math.round(event.beta-90)),36,document.body);
  892. self.gyroEvent=event;
  893. }
  894. },
  895. stereoEffect : function ( renderer ) {
  896. // API
  897. /*Angle of deviation*/
  898. this.separation = 1;
  899. /*
  900. * Distance to the non-parallax or projection plane
  901. */
  902. this.focalLength = 15;
  903. // internals
  904. var _width, _height;
  905. var _position = new THREE.Vector3();
  906. var _quaternion = new THREE.Quaternion();
  907. var _scale = new THREE.Vector3();
  908. var _cameraL = new THREE.PerspectiveCamera();
  909. var _cameraR = new THREE.PerspectiveCamera();
  910. var _fov;
  911. var _outer, _inner, _top, _bottom;
  912. var _ndfl, _halfFocalWidth, _halfFocalHeight;
  913. var _innerFactor, _outerFactor;
  914. // initialization
  915. renderer.autoClear = false;
  916. this.setSize = function (width, height) {
  917. _width = width / 2;
  918. _height = height;
  919. renderer.setSize(width, height);
  920. };
  921. this.render = function (scene, camera) {
  922. scene.updateMatrixWorld();
  923. void 0 === camera.parent && camera.updateMatrixWorld();
  924. camera.matrixWorld.decompose(_position, _quaternion, _scale);
  925. // Stereo frustum calculation
  926. // Effective fov of the camera
  927. _fov = THREE.Math.radToDeg(2 * Math.atan(Math.tan(THREE.Math.degToRad(camera.fov) * 0.5)));
  928. _ndfl = camera.near / this.focalLength;
  929. _halfFocalHeight = Math.tan(THREE.Math.degToRad(_fov) * 0.5) * this.focalLength;
  930. _halfFocalWidth = _halfFocalHeight * 0.5 * camera.aspect;
  931. _top = _halfFocalHeight * _ndfl;
  932. _bottom = -_top;
  933. _innerFactor = ( _halfFocalWidth + this.separation / 2.0 ) / ( _halfFocalWidth * 2.0 );
  934. _outerFactor = 1.0 - _innerFactor;
  935. _outer = _halfFocalWidth * 2.0 * _ndfl * _outerFactor;
  936. _inner = _halfFocalWidth * 2.0 * _ndfl * _innerFactor;
  937. // left
  938. _cameraL.projectionMatrix.makePerspective(
  939. -_outer,
  940. _inner,
  941. _top,
  942. _bottom,
  943. camera.near,
  944. camera.far
  945. );
  946. _cameraL.position.copy(_position);
  947. _cameraL.quaternion.copy(_quaternion);
  948. _cameraL.translateX(-this.separation / 2.0);
  949. // right
  950. _cameraR.projectionMatrix.makePerspective(
  951. -_inner,
  952. _outer,
  953. _top,
  954. _bottom,
  955. camera.near,
  956. camera.far
  957. );
  958. _cameraR.position.copy(_position);
  959. _cameraR.quaternion.copy(_quaternion);
  960. _cameraR.translateX(this.separation / 2.0);
  961. //
  962. renderer.setViewport(0, 0, _width * 2, _height);
  963. //renderer.clear();
  964. renderer.setViewport(0, 0, _width, _height);
  965. renderer.render(scene, _cameraL);
  966. renderer.setViewport(_width, 0, _width, _height);
  967. renderer.render(scene, _cameraR);
  968. };
  969. return this;
  970. },
  971. orbitControls : function(object,domElement){
  972. var controls=function ( object, domElement ) {
  973. this.domElement = ( void 0 !== domElement ) ? domElement : document;
  974. this.object = object;
  975. this.enable = false;
  976. this.target = new THREE.Vector3();
  977. // How far you can orbit vertically, upper and lower limits.
  978. // Range is 0 to Math.PI radians.
  979. this.minPolarAngle = 0; // radians
  980. this.maxPolarAngle = Math.PI; // radians
  981. // How far you can orbit horizontally, upper and lower limits.
  982. // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ].
  983. this.minAzimuthAngle = -Infinity; // radians
  984. this.maxAzimuthAngle = Infinity; // radians
  985. // Set to true to enable damping (inertia)
  986. // If damping is enabled, you must call controls.update() in your animation loop
  987. this.enableDamping = false;
  988. this.dampingFactor = 0.25;
  989. this.rotateSpeed = 0.3;
  990. // Set to true to automatically rotate around the target
  991. // If auto-rotate is enabled, you must call controls.update() in your animation loop
  992. this.autoRotate = false;
  993. this.autoRotateSpeed = 1.0; // 30 seconds per round when fps is 60
  994. this.deviceOrientation = {};
  995. this.screenOrientation = 0;
  996. var scope = this;
  997. //scope.object.lookAt(new THREE.Vector3(0,0,1));
  998. scope.defaultDirectionOfRotation = true;
  999. scope.gyroEnable = true;
  1000. scope.usingGyro = AVR.OS.isMobile() ? true : false;
  1001. scope._defaultTargetY = scope.target.y;
  1002. scope._defaultCameraFov = scope.object.fov;
  1003. scope._defaultCameraY = scope.object.position.y;
  1004. var changeEvent = {type: 'change'};
  1005. var startEvent = {type: 'start'};
  1006. var endEvent = {type: 'end'};
  1007. var EPS = 0.000001;
  1008. // current position in spherical coordinates
  1009. var spherical = new THREE.Spherical();
  1010. var sphericalDelta = new THREE.Spherical();
  1011. var rotateStart = new THREE.Vector2();
  1012. var rotateEnd = new THREE.Vector2();
  1013. var rotateDelta = new THREE.Vector2();
  1014. var setObjectQuaternion = function () {
  1015. var zee = new THREE.Vector3(0, 0, 1);
  1016. var euler = new THREE.Euler();
  1017. var q0 = new THREE.Quaternion();
  1018. // - PI/2 around the x-axis
  1019. var q1 = new THREE.Quaternion(-Math.sqrt(0.5), 0, 0, Math.sqrt(0.5));
  1020. //beta=beta-180;
  1021. return function (quaternion, alpha, beta, gamma, orient) {
  1022. // 'ZXY' for the device, but 'YXZ' for us
  1023. euler.set(beta, alpha, -gamma, 'YXZ');
  1024. // orient the device
  1025. quaternion.setFromEuler(euler);
  1026. // camera looks out the back of the device, not the top
  1027. quaternion.multiply(q1);
  1028. // adjust for screen orientation
  1029. quaternion.multiply(q0.setFromAxisAngle(zee, -orient));
  1030. }
  1031. }();
  1032. // this method is exposed, but perhaps it would be better if we can make it private...
  1033. this.update = function () {
  1034. var offset = new THREE.Vector3();
  1035. // so camera.up is the orbit axis
  1036. var quat = new THREE.Quaternion().setFromUnitVectors(object.up, new THREE.Vector3(0, 1, 0));
  1037. var quatInverse = quat.clone().inverse();
  1038. var lastPosition = new THREE.Vector3();
  1039. var lastQuaternion = new THREE.Quaternion();
  1040. var lastGamma = 0, lastBeta = 0;
  1041. var tempAlpha = 0, tempBeta = 0, tempGamma = 0;
  1042. return function update(param) {
  1043. if (!scope.enable) {
  1044. return;
  1045. }
  1046. param = param || {};
  1047. var alpha = scope.deviceOrientation.alpha ? THREE.Math.degToRad(void 0 === scope.beginAlpha ? scope.deviceOrientation.alpha :scope.deviceOrientation.alpha-scope.beginAlpha) : 0; // Z
  1048. var beta = scope.deviceOrientation.beta ? THREE.Math.degToRad(scope.deviceOrientation.beta) : 0; // X'
  1049. var gamma = scope.deviceOrientation.gamma ? THREE.Math.degToRad(scope.deviceOrientation.gamma) : 0; // Y''
  1050. var orient = scope.screenOrientation ? THREE.Math.degToRad(scope.screenOrientation) : 0; // O
  1051. if (scope.gyroEnable) {
  1052. tempAlpha = alpha, tempBeta = beta, tempGamma = gamma;
  1053. } else {
  1054. alpha = tempAlpha, beta = tempBeta, gamma = tempGamma;
  1055. }
  1056. /*AVR.msgBox("alpha="+Math.round(scope.deviceOrientation.alpha-scope.beginAlpha)+
  1057. ',beta='+Math.round(scope.deviceOrientation.beta)+
  1058. ',gamma='+Math.round(scope.deviceOrientation.gamma),36,document.body)*/
  1059. //AVR.msgBox("x="+scope.object.position.x.toFixed(2)+",y="+scope.object.position.y.toFixed(2)+",z="+scope.object.position.z.toFixed(2) ,0.5,document.body);
  1060. var currentQ = new THREE.Quaternion().copy(scope.object.quaternion);
  1061. setObjectQuaternion(currentQ, alpha, beta, gamma, orient);
  1062. var currentAngle = Quat2Angle(currentQ.x, currentQ.y, currentQ.z, currentQ.w);
  1063. if (!param.init) {
  1064. // currentAngle.z = Left-right
  1065. // currentAngle.y = Up-down
  1066. rotateLeft((lastGamma - currentAngle.z));
  1067. rotateUp((lastBeta - currentAngle.y));
  1068. }
  1069. lastBeta = currentAngle.y;
  1070. lastGamma = currentAngle.z;
  1071. var position = scope.object.position;
  1072. offset.copy(position).sub(scope.target);
  1073. // rotate offset to "y-axis-is-up" space
  1074. offset.applyQuaternion(quat);
  1075. // angle from z-axis around y-axis
  1076. spherical.setFromVector3(offset);
  1077. if (scope.autoRotate) {
  1078. rotateLeft(getAutoRotationAngle());
  1079. }
  1080. spherical.theta += sphericalDelta.theta;
  1081. spherical.phi += sphericalDelta.phi;
  1082. // restrict theta to be between desired limits
  1083. spherical.theta = Math.max(scope.minAzimuthAngle, Math.min(scope.maxAzimuthAngle, spherical.theta));
  1084. // restrict phi to be between desired limits
  1085. spherical.phi = Math.max(scope.minPolarAngle, Math.min(scope.maxPolarAngle, spherical.phi));
  1086. spherical.makeSafe();
  1087. offset.setFromSpherical(spherical);
  1088. // rotate offset back to "camera-up-vector-is-up" space
  1089. offset.applyQuaternion(quatInverse);
  1090. position.copy(scope.target).add(offset);
  1091. scope.object.lookAt(scope.target);
  1092. if (scope.enableDamping === true) {
  1093. sphericalDelta.theta *= ( 1 - scope.dampingFactor );
  1094. sphericalDelta.phi *= ( 1 - scope.dampingFactor );
  1095. } else {
  1096. sphericalDelta.set(0, 0, 0);
  1097. }
  1098. // update condition is:
  1099. // min(camera displacement, camera rotation in radians)^2 > EPS
  1100. // using small-angle approximation cos(x/2) = 1 - x^2 / 8
  1101. if (lastPosition.distanceToSquared(scope.object.position) > EPS ||
  1102. 8 * ( 1 - lastQuaternion.dot(scope.object.quaternion) ) > EPS) {
  1103. scope.dispatchEvent(changeEvent);
  1104. lastPosition.copy(scope.object.position);
  1105. lastQuaternion.copy(scope.object.quaternion);
  1106. return true;
  1107. }
  1108. return false;
  1109. };
  1110. }();
  1111. function getAutoRotationAngle() {
  1112. return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
  1113. }
  1114. function rotateLeft(angle) {
  1115. if (scope.defaultDirectionOfRotation) {
  1116. if (scope.usingGyro) {
  1117. sphericalDelta.theta -= angle;
  1118. } else {
  1119. sphericalDelta.theta += angle;
  1120. }
  1121. } else {
  1122. sphericalDelta.theta -= angle;
  1123. }
  1124. }
  1125. function rotateUp(angle) {
  1126. if (scope.defaultDirectionOfRotation) {
  1127. if (scope.usingGyro) {
  1128. sphericalDelta.phi -= angle;
  1129. } else {
  1130. sphericalDelta.phi += angle;
  1131. }
  1132. } else {
  1133. sphericalDelta.phi -= angle;
  1134. }
  1135. }
  1136. function Quat2Angle(x, y, z, w) {
  1137. var pitch, roll, yaw;
  1138. var test = x * y + z * w;
  1139. if (test > 0.499) { // singularity at north pole
  1140. yaw = 2 * Math.atan2(x, w);
  1141. pitch = Math.PI / 2;
  1142. roll = 0;
  1143. var euler = new THREE.Vector3(pitch, roll, yaw);
  1144. return euler;
  1145. }
  1146. if (test < -0.499) { // singularity at south pole
  1147. yaw = -2 * Math.atan2(x, w);
  1148. pitch = -Math.PI / 2;
  1149. roll = 0;
  1150. var euler = new THREE.Vector3(pitch, roll, yaw);
  1151. return euler;
  1152. }
  1153. var sqx = x * x;
  1154. var sqy = y * y;
  1155. var sqz = z * z;
  1156. yaw = Math.atan2(2 * y * w - 2 * x * z, 1 - 2 * sqy - 2 * sqz);
  1157. pitch = Math.asin(2 * test);
  1158. roll = Math.atan2(2 * x * w - 2 * y * z, 1 - 2 * sqx - 2 * sqz);
  1159. //AVR.msgBox("yaw="+yaw.toFixed(2)+",pitch="+pitch.toFixed(2)+",roll="+roll.toFixed(2) ,0.5,document.body);
  1160. var euler = new THREE.Vector3(pitch, roll, yaw);
  1161. return euler;
  1162. }
  1163. var mouseDown = false;
  1164. function mousedown(event) {
  1165. mouseDown = true;
  1166. rotateStart.set(event.clientX, event.clientY);
  1167. }
  1168. function mousemove(event) {
  1169. rotateEnd.set(event.clientX, event.clientY);
  1170. rotateDelta.subVectors(rotateEnd, rotateStart);
  1171. // rotating across whole screen goes 360 degrees around
  1172. var clientWidth = (void 0 !== scope.domElement.clientWidth) ? scope.domElement.clientWidth : window.innerWidth;
  1173. rotateLeft(2 * Math.PI * rotateDelta.x / clientWidth * scope.rotateSpeed);
  1174. // rotating up and down along whole screen attempts to go 360, but limited to 180
  1175. var clientHeight = (void 0 !== scope.domElement.clientHeight) ? scope.domElement.clientHeight : window.innerHeight;
  1176. rotateUp(2 * Math.PI * rotateDelta.y / clientHeight * scope.rotateSpeed);
  1177. rotateStart.copy(rotateEnd);
  1178. scope.update();
  1179. }
  1180. function mouseup(event) {
  1181. mouseDown = false;
  1182. }
  1183. function touchstart(event) {
  1184. //console.log( 'handleTouchStartRotate' );
  1185. rotateStart.set(event.touches[0].pageX, event.touches[0].pageY);
  1186. scope.usingGyro = false;
  1187. }
  1188. function touchmove(event) {
  1189. //console.log( 'handleTouchMoveRotate' );
  1190. event.preventDefault();
  1191. rotateEnd.set(event.touches[0].pageX, event.touches[0].pageY);
  1192. rotateDelta.subVectors(rotateEnd, rotateStart);
  1193. // rotating across whole screen goes 360 degrees around
  1194. var clientWidth = (void 0 != scope.domElement.clientWidth) ? scope.domElement.clientWidth : window.innerWidth;
  1195. rotateLeft(2 * Math.PI * rotateDelta.x / clientWidth * scope.rotateSpeed);
  1196. // rotating up and down along whole screen attempts to go 360, but limited to 180
  1197. var clientHeight = (void 0 !== scope.domElement.clientHeight) ? scope.domElement.clientHeight : window.innerHeight;
  1198. rotateUp(2 * Math.PI * rotateDelta.y / clientHeight * scope.rotateSpeed);
  1199. rotateStart.copy(rotateEnd);
  1200. scope.update();
  1201. scope.usingGyro = false;
  1202. }
  1203. function touchend(event) {
  1204. //console.log( 'handleTouchEnd' );
  1205. scope.usingGyro = AVR.OS.isMobile() ? true : false;
  1206. }
  1207. function deviceorientation(event) {
  1208. scope.deviceOrientation = event;
  1209. void 0 === scope.beginAlpha && (scope.beginAlpha = scope.deviceOrientation.alpha);
  1210. }
  1211. function orientationchange(event) {
  1212. scope.screenOrientation = window.orientation || 0;
  1213. }
  1214. window.addEventListener('orientationchange', orientationchange, false);
  1215. window.addEventListener('deviceorientation', deviceorientation, false);
  1216. this.gyroFreeze = function () {
  1217. scope.gyroEnable = false;
  1218. };
  1219. this.gyroUnfreeze = function () {
  1220. scope.gyroEnable = true;
  1221. };
  1222. this.domElement.addEventListener("mousedown", mousedown, false);
  1223. this.domElement.addEventListener('mousemove', function (e) {
  1224. if (scope.enable && mouseDown) {
  1225. mousemove(e);
  1226. }
  1227. }, false);
  1228. this.domElement.addEventListener("mouseup", mouseup, false);
  1229. this.domElement.addEventListener('touchstart', touchstart, false);
  1230. this.domElement.addEventListener('touchend', touchend, false);
  1231. this.domElement.addEventListener('touchmove', touchmove, false);
  1232. // set mousemove base point is dom center
  1233. var clientWidth = (void 0 !== this.domElement.clientWidth) ? this.domElement.clientWidth : window.innerWidth;
  1234. var clientHeight = (void 0 !== this.domElement.clientHeight) ? this.domElement.clientHeight : window.innerHeight;
  1235. rotateStart.set(clientWidth / 2, clientHeight / 2);
  1236. // force an update at start
  1237. rotateLeft(THREE.Math.degToRad(-90));
  1238. setTimeout(function () {
  1239. scope.enable = true;
  1240. scope.update({init: true});
  1241. }, 0);
  1242. return this;
  1243. }
  1244. controls.prototype=Object.create(THREE.EventDispatcher.prototype);
  1245. controls.prototype.constructor=controls;
  1246. return new controls(object,domElement);
  1247. },
  1248. setCameraPara: function (that, cameraPara, cameraPosition) {
  1249. that.cameraPara = {"fov": 90, "aspect": window.innerWidth / window.innerHeight, "near": 0.001, "far": 1000};
  1250. that.cameraPosition = {"x": 0, "y": 0, "z": 0};
  1251. if (cameraPara) {
  1252. for (var property in cameraPara) {
  1253. that.cameraPara[property] = cameraPara[property];
  1254. }
  1255. }
  1256. if (cameraPosition) {
  1257. for (var property in cameraPosition) {
  1258. that.cameraPosition[property] = cameraPosition[property];
  1259. }
  1260. }
  1261. },
  1262. formatSeconds: function (value) {
  1263. var theTime = parseInt(value);// 秒
  1264. var theTime1 = 0;// 分
  1265. var theTime2 = 0;// 小时
  1266. if (theTime > 60) {
  1267. theTime1 = parseInt(theTime / 60);
  1268. theTime = parseInt(theTime % 60);
  1269. if (theTime1 > 60) {
  1270. theTime2 = parseInt(theTime1 / 60);
  1271. theTime1 = parseInt(theTime1 % 60);
  1272. }
  1273. }
  1274. var result = "" + ((parseInt(theTime) < 10) ? "0" + parseInt(theTime) : parseInt(theTime));
  1275. if (theTime1 >= 0 && theTime2 > 0) {
  1276. result = ((parseInt(theTime2) < 10) ? "0" + parseInt(theTime2) : parseInt(theTime2)) + ":" + ((parseInt(theTime1) < 10) ? "0" + parseInt(theTime1) : parseInt(theTime1)) + ":" + result;
  1277. } else if (theTime1 > 0 && theTime2 == 0) {
  1278. if (theTime1 == 60) {
  1279. result = "01:00:" + result;
  1280. } else {
  1281. result = ((parseInt(theTime1) < 10) ? "0" + parseInt(theTime1) : parseInt(theTime1)) + ":" + result;
  1282. }
  1283. } else {
  1284. if (theTime == 60) {
  1285. result = "01:00";
  1286. } else {
  1287. result = "00:" + result;
  1288. }
  1289. }
  1290. return result;
  1291. },
  1292. cameraVector:function (camera,times) {
  1293. var vector=new THREE.Vector3(0,0,-1);
  1294. var lookAt=vector.applyQuaternion(camera.quaternion);
  1295. var lookAtVector=lookAt.clone();
  1296. var timesVector=new THREE.Vector3();
  1297. if(times) {
  1298. timesVector.x = lookAt.x * times;
  1299. timesVector.y = lookAt.y * times;
  1300. timesVector.z = lookAt.z * times;
  1301. }
  1302. return {'vector':lookAtVector,'timesVector':timesVector};
  1303. },
  1304. bindCameraEvent:function (vr,options) {
  1305. options=options||{trigger:function(e){},empty:function(e){},move:function(e){}};
  1306. var that = this;
  1307. var scale=options.scale || 0.05;
  1308. var vectorRadius = options.vectorRadius;
  1309. var radius = vectorRadius*scale/2;
  1310. var tube = vectorRadius*(scale/6);
  1311. var pointSize=vectorRadius*(scale/8);
  1312. var radialSegments = 2;
  1313. var tubularSegments = options.tubularSegments || 60;
  1314. var speed=options.speed || 36;
  1315. var ControlGroup = new THREE.Group();
  1316. ControlGroup.name = "__controlHandle";
  1317. var waitGeometry = new THREE.TorusGeometry(radius, tube, radialSegments, tubularSegments, 2 * Math.PI);
  1318. var waitMaterial = [];
  1319. for (var i = 0; i < waitGeometry.faces.length / 2; i++) {
  1320. waitMaterial[i] = new THREE.MeshBasicMaterial({color: 0xcccccc,depthTest:false});
  1321. }
  1322. var faceId = 0;
  1323. var uv = [new THREE.Vector2(0, 0), new THREE.Vector2(1, 0), new THREE.Vector2(1, 1), new THREE.Vector2(0, 1)];
  1324. for (var i = 0, l = waitGeometry.faces.length; i < l; i += 2) {
  1325. waitGeometry.faces[i].materialIndex = faceId;
  1326. waitGeometry.faces[i + 1].materialIndex = faceId;
  1327. waitGeometry.faceVertexUvs[0][i] = [uv[3], uv[0], uv[2]];
  1328. waitGeometry.faceVertexUvs[0][i + 1] = [uv[0], uv[1], uv[2]];
  1329. faceId++;
  1330. }
  1331. var wait = new THREE.Mesh(waitGeometry, waitMaterial);
  1332. wait.lookAt(vr.camera.position)
  1333. wait.name = "__wait";
  1334. wait.visible=false;
  1335. ControlGroup.add(wait);
  1336. var cameraPointer = new THREE.Mesh(new THREE.CircleGeometry(tube, 4), new THREE.MeshBasicMaterial({
  1337. color: 0xffffff,wireframe: true,depthTest:false
  1338. }));
  1339. cameraPointer.lookAt(vr.camera.position);
  1340. cameraPointer.name = "__focus";
  1341. cameraPointer.material.depthTest=false;
  1342. cameraPointer.visible=false;
  1343. ControlGroup.add(cameraPointer);
  1344. ControlGroup.position.set(0, 0, 0.1)
  1345. var timer = null;
  1346. var lastCameraVector=new THREE.Vector3();
  1347. var updatePosition = function () {
  1348. ControlGroup.lookAt(vr.camera.position);
  1349. var v=that.cameraVector(vr.camera,vectorRadius);
  1350. cameraPointer.visible = true;
  1351. ControlGroup.position.set(v.timesVector.x,v.timesVector.y,v.timesVector.z);
  1352. //在视点坐标系中形成射线,射线的起点向量是照相机, 射线的方向向量是照相机到点击的点,这个向量应该归一标准化。
  1353. var raycaster = new THREE.Raycaster(vr.camera.position, v.vector);
  1354. //射线和模型求交,选中一系列直线
  1355. var intersects = raycaster.intersectObjects(vr.scene.children,true);
  1356. if(intersects.length) {
  1357. options.move(intersects);
  1358. }else {
  1359. options.empty(intersects);
  1360. }
  1361. }
  1362. var Ctimer=null;
  1363. var hover=function (e) {
  1364. wait.visible=true;
  1365. var offset = 0;
  1366. var mIndex = 0;
  1367. if(!Ctimer) {
  1368. Ctimer = setInterval(function () {
  1369. if (mIndex < waitGeometry.faces.length / 4) {
  1370. waitMaterial[mIndex].color = new THREE.Color(0xff0000)
  1371. waitGeometry.needsUpdate = true;
  1372. waitGeometry.faces[offset].materialIndex = mIndex;
  1373. waitGeometry.faces[offset + 1].materialIndex = mIndex;
  1374. waitGeometry.faceVertexUvs[0][offset] = [uv[3], uv[0], uv[2]];
  1375. waitGeometry.faceVertexUvs[0][offset + 1] = [uv[0], uv[1], uv[2]];
  1376. offset += 2;
  1377. } else {
  1378. clearInterval(Ctimer);
  1379. options.trigger(e);
  1380. }
  1381. mIndex++;
  1382. }, speed);
  1383. }
  1384. }
  1385. var leave=function (e) {
  1386. clearInterval(Ctimer);
  1387. Ctimer=null;
  1388. faceId = 0;
  1389. for (var i = 0, l = waitGeometry.faces.length; i < l; i += 2) {
  1390. waitMaterial[faceId].color = new THREE.Color(0xcccccc)
  1391. waitGeometry.needsUpdate = true;
  1392. waitGeometry.faces[i].materialIndex = faceId;
  1393. waitGeometry.faces[i + 1].materialIndex = faceId;
  1394. waitGeometry.faceVertexUvs[0][i] = [uv[3], uv[0], uv[2]];
  1395. waitGeometry.faceVertexUvs[0][i + 1] = [uv[0], uv[1], uv[2]];
  1396. faceId++;
  1397. }
  1398. wait.visible=false;
  1399. }
  1400. vr.VRObject.add(ControlGroup);
  1401. return {controlGroup:ControlGroup,updatePosition:updatePosition,hover:hover,leave:leave};
  1402. },
  1403. screenPosTo3DCoordinate:function(event,container,camera) {
  1404. var x = event.clientX - container.offsetLeft;
  1405. var y = event.clientY - container.offsetTop;
  1406. //console.log(x,y);
  1407. var W = container.clientWidth;
  1408. var H = container.clientHeight;
  1409. var mouse = new THREE.Vector2();
  1410. mouse.x = 2 * x / W - 1;
  1411. mouse.y = 1 - 2 * y / H;
  1412. var vector = new THREE.Vector3(mouse.x, mouse.y, 0).unproject(camera);
  1413. return vector;
  1414. },
  1415. objectPosToScreenPos:function (object,container,camera) {
  1416. var vector = new THREE.Vector3();
  1417. vector.setFromMatrixPosition(object.matrixWorld).project(camera);
  1418. var x2hat = vector.x, y2hat = vector.y;
  1419. var W = container.clientWidth;
  1420. var H = container.clientHeight;
  1421. var pos = new THREE.Vector2();
  1422. pos.x = (W / 2) * (x2hat + 1);
  1423. pos.y = (H / 2) * (1 - y2hat);
  1424. return pos;
  1425. },
  1426. fullscreen: function (el) {
  1427. var isFullscreen = document.fullScreen || document.mozFullScreen || document.webkitIsFullScreen || false;
  1428. if (!isFullscreen) {//进入全屏,多重短路表达式
  1429. (el.requestFullscreen && el.requestFullscreen()) ||
  1430. (el.mozRequestFullScreen && el.mozRequestFullScreen()) ||
  1431. (el.webkitRequestFullscreen && el.webkitRequestFullscreen()) || (el.msRequestFullscreen && el.msRequestFullscreen());
  1432. } else { //退出全屏,三目运算符
  1433. document.exitFullscreen ? document.exitFullscreen() :
  1434. document.mozCancelFullScreen ? document.mozCancelFullScreen() :
  1435. document.webkitExitFullscreen ? document.webkitExitFullscreen() : '';
  1436. }
  1437. },
  1438. isFullscreen: function (callback) {
  1439. var that = this;
  1440. function c_back(e) {
  1441. callback(that.isFullscreen());
  1442. }
  1443. if (typeof callback === "function") {
  1444. //监听状态变化
  1445. window.removeEventListener('fullscreenchange', c_back, false);
  1446. window.removeEventListener('webkitfullscreenchange', c_back, false);
  1447. window.removeEventListener('mozfullscreenchange', c_back, false);
  1448. window.removeEventListener('msFullscreenChange', c_back, false);
  1449. window.addEventListener('fullscreenchange', c_back, false);
  1450. window.addEventListener('webkitfullscreenchange', c_back, false);
  1451. window.addEventListener('mozfullscreenchange', c_back, false);
  1452. window.addEventListener('msFullscreenChange', c_back, false);
  1453. } else {
  1454. return document.fullscreenElement ||
  1455. document.msFullscreenElement ||
  1456. document.mozFullScreenElement ||
  1457. document.webkitFullscreenElement || false;
  1458. }
  1459. },
  1460. videoToolBar: function (container) {
  1461. var pre = "_videoToolBar";
  1462. var toolbar = this.createTag('div', {
  1463. 'style': '-moz-user-select:none;-webkit-user-select:none;user-select:none;position:absolute;background:rgba(0,0,0,.6);z-index:9999;width:100%;height:2.2rem;bottom:0rem',
  1464. 'class': pre + 'Area'
  1465. });
  1466. var btn = this.createTag('div', {
  1467. 'style': 'position:inherit;border-top:0.6rem solid transparent;border-left:1rem solid white;border-bottom:0.6rem solid transparent;bottom:0.25rem;left:1rem;color:#fff;font-weight:800;cursor:pointer',
  1468. 'class': pre + 'Btn'
  1469. })
  1470. toolbar.appendChild(btn);
  1471. var timeInfo = this.createTag('div', {
  1472. 'style': 'position:inherit;bottom:0.25rem;left:2.8rem;color:#fff;font-size:0.75rem'
  1473. });
  1474. var curTime = this.createTag('span', null, {
  1475. 'innerText': "00:00"
  1476. });
  1477. timeInfo.appendChild(curTime);
  1478. var splitTime = this.createTag('span', null, {
  1479. 'innerText': '/'
  1480. });
  1481. timeInfo.appendChild(splitTime);
  1482. var totalTime = this.createTag('span', null, {
  1483. 'innerText': '00:00'
  1484. });
  1485. timeInfo.appendChild(totalTime);
  1486. toolbar.appendChild(timeInfo);
  1487. var gyroBtn = this.createTag('div', {
  1488. 'style': 'border:0.125rem solid white;border-radius:1rem;width:1.4rem;height:1rem;position:inherit;right:3.5rem;line-height:1rem;bottom:0.25rem;cursor:pointer'
  1489. });
  1490. var circle1 = this.createTag('div', {
  1491. 'style': 'position:inherit;width:1.235rem;height:0.4rem;line-height:0.4rem;border:0.0625rem solid white;border-radius:0.6rem/0.2rem;margin-top:0.25rem;margin-left:0.055rem;'
  1492. });
  1493. gyroBtn.appendChild(circle1);
  1494. var circle2 = this.createTag('div', {
  1495. 'style': 'position:inherit;width:1rem;height:0.4rem;line-height:0.4rem;border:0.0625rem solid white;border-radius:0.6rem/0.2rem;margin-top:0.25rem;margin-left:0.175rem;transform:rotate(90deg)'
  1496. })
  1497. gyroBtn.appendChild(circle2);
  1498. toolbar.appendChild(gyroBtn);
  1499. var vrBtn = this.createTag('div', {
  1500. 'style': "position:inherit;right:1rem;width:1.4rem;height:1rem;line-height:1rem;border:0.125rem solid white;border-radius:0.125rem;bottom:0.25rem;text-align:center;font-weight:800;color:#fff;font-size:0.75rem;cursor:pointer"
  1501. }, {
  1502. 'innerText': "VR"
  1503. });
  1504. toolbar.appendChild(vrBtn);
  1505. var progressBar = this.createTag('div', {
  1506. 'style': 'position:inherit;top:0rem;width:100%;height:0.3125rem;background:rgba(255,255,255,.7);cursor:pointer'
  1507. });
  1508. var loaded_progress = this.createTag('div', {
  1509. 'style': 'position:inherit;width:0%;height:0.3125rem;background:rgba(255,255,255,.7)'
  1510. });
  1511. progressBar.appendChild(loaded_progress);
  1512. var play_progress = this.createTag('div', {
  1513. 'style': 'position:inherit;width:0%;height:0.3125rem;background:rgba(28, 175, 252,.8)'
  1514. });
  1515. progressBar.appendChild(play_progress);
  1516. toolbar.appendChild(progressBar);
  1517. container.appendChild(toolbar);
  1518. var voice_bar = this.createTag('div', {
  1519. 'style': '-moz-user-select:none;-webkit-user-select:none;user-select:none;position:absolute;width:2rem;height:100%;background:rgba(0,0,0,0);right:0rem;top:0rem;text-align:center;display:none'
  1520. });
  1521. var voice_slide_bar = this.createTag('div', {
  1522. 'style': 'position:inherit;width:0.25rem;background:rgba(255,255,255,.1);height:100%;left:0.875rem;cursor:pointer;'
  1523. });
  1524. voice_bar.appendChild(voice_slide_bar);
  1525. var voice_cur_slide = this.createTag('div', {
  1526. 'style': 'position:inherit;width:100%;background:rgba(255, 255, 255,.6);bottom:0rem;'
  1527. });
  1528. voice_slide_bar.appendChild(voice_cur_slide);
  1529. var voice_slide_btn = this.createTag('div', {
  1530. 'style': 'position:inherit;width:1rem;height:1rem;border-radius:1rem;background:rgba(255,255,255,0.6);top:0rem;margin-left:-0.375rem;cursor:pointer'
  1531. });
  1532. voice_cur_slide.appendChild(voice_slide_btn);
  1533. container.appendChild(voice_bar);
  1534. return {
  1535. 'toolbar': toolbar,
  1536. 'btn': btn,
  1537. 'timeInfo': timeInfo,
  1538. 'curTime': curTime,
  1539. 'splitTime': splitTime,
  1540. 'totalTime': totalTime,
  1541. 'vrBtn': vrBtn,
  1542. 'progressBar': progressBar,
  1543. 'loadedProgress': loaded_progress,
  1544. 'playProgress': play_progress,
  1545. 'gyroBtn': gyroBtn,
  1546. "circle1": circle1,
  1547. "circle2": circle2,
  1548. "voice_bar": voice_bar
  1549. };
  1550. },
  1551. msgBox: function (msg, timeout, container) {
  1552. var msgbox = this.createTag('div', {
  1553. 'style': 'position:absolute;bottom:50%;width:100%;padding:0.25rem;background:rgba(0,0,0,.6);color:#fff;text-align:center;'
  1554. }, {
  1555. 'innerHTML': msg
  1556. });
  1557. container.appendChild(msgbox);
  1558. setTimeout(function () {
  1559. msgbox.remove();
  1560. }, timeout * 1000);
  1561. },
  1562. isMobileDevice: function (deviceType) {
  1563. var sUserAgent = navigator.userAgent.toLowerCase();
  1564. if (deviceType) {
  1565. return (sUserAgent.match(/ipad/i) || sUserAgent.match(/iphone os/i) || sUserAgent.match(/midp/i) ||
  1566. sUserAgent.match(/rv:1.2.3.4/i) || sUserAgent.match(/ucweb/i) || sUserAgent.match(/android/i) ||
  1567. sUserAgent.match(/windows ce/i) || sUserAgent.match(/windows mobile/i))
  1568. }
  1569. var bIsIpad = sUserAgent.match(/ipad/i) == "ipad";
  1570. var bIsIphoneOs = sUserAgent.match(/iphone os/i) == "iphone os";
  1571. var bIsMidp = sUserAgent.match(/midp/i) == "midp";
  1572. var bIsUc7 = sUserAgent.match(/rv:1.2.3.4/i) == "rv:1.2.3.4";
  1573. var bIsUc = sUserAgent.match(/ucweb/i) == "ucweb";
  1574. var bIsAndroid = sUserAgent.match(/android/i) == "android";
  1575. var bIsCE = sUserAgent.match(/windows ce/i) == "windows ce";
  1576. var bIsWM = sUserAgent.match(/windows mobile/i) == "windows mobile";
  1577. if (bIsIpad || bIsIphoneOs || bIsMidp || bIsUc7 || bIsUc || bIsAndroid || bIsCE || bIsWM) {
  1578. //dconsole.log("phone");
  1579. return true;
  1580. } else {
  1581. //console.log("pc");
  1582. return false;
  1583. }
  1584. },
  1585. bindOrientationEnevt: function (that,target) {
  1586. if (void 0 === that.controls) {
  1587. if (AVR.isMobileDevice()) {
  1588. target.y = -1;
  1589. }
  1590. that.controls = AVR.orbitControls(that.camera, that.renderer.domElement);
  1591. var target = new THREE.Vector3(target.x, target.y, target.z);
  1592. that.controls.target = target;
  1593. that.controls.update(that.clock.getDelta());
  1594. }
  1595. },
  1596. //横屏判断
  1597. isCrossScreen: function (callback) {
  1598. var that = this;
  1599. if (typeof callback === "function") {
  1600. function c_back(e) {
  1601. callback(that.isCrossScreen());
  1602. }
  1603. window.removeEventListener('orientationchange', c_back, false);
  1604. window.addEventListener('orientationchange', c_back, false);
  1605. } else {
  1606. if (window.orientation == 180 || window.orientation == 0) {
  1607. /*Vertical screen*/
  1608. return false;
  1609. }
  1610. if (window.orientation == 90 || window.orientation == -90) {
  1611. /*is Cross Screen*/
  1612. return true;
  1613. }
  1614. }
  1615. },
  1616. initDomStyle: function (container) {
  1617. document.body.style.overflow = "hidden";
  1618. document.body.style.margin = 0;
  1619. document.body.style.padding = 0;
  1620. container.style.position = "absolute";
  1621. container.style.width = "100%";
  1622. container.style.height = "100%";
  1623. container.style.left = "0px";
  1624. container.style.top = "0px";
  1625. container.style.overflow = "hidden";
  1626. document.oncontextmenu = function () {
  1627. return false;
  1628. };
  1629. document.onkeydown = function () {
  1630. if (!this.debug && window.event && window.event.keyCode == 123) {
  1631. event.keyCode = 0;
  1632. event.returnValue = false;
  1633. return false;
  1634. }
  1635. };
  1636. },
  1637. createTag: function (tag, attr, objs) {
  1638. var oMeta = document.createElement(tag);
  1639. if (attr && typeof attr === "object") {
  1640. for (var k in attr) {
  1641. oMeta.setAttribute(k, attr[k]);
  1642. }
  1643. }
  1644. if (objs && typeof objs === "object") {
  1645. for (var k in objs) {
  1646. oMeta[k] = objs[k];
  1647. }
  1648. }
  1649. return oMeta;
  1650. },
  1651. OS: {
  1652. weixin: navigator.userAgent.indexOf('MicroMessenger') > -1,
  1653. android: /android/i.test(navigator.userAgent.toLowerCase()),
  1654. ios: /(iphone|ipad|ipod|ios)/i.test(navigator.userAgent.toLowerCase()),
  1655. googlePixel: navigator.userAgent.match(/;\sPixel\sBuild\//),
  1656. MiOS: navigator.userAgent.match(/;\sMI\s\d\sBuild\//),
  1657. samsungOS: navigator.userAgent.match(/;\sSM\-\w+\sBuild\//),
  1658. isGooglePixel: function () {
  1659. return this.googlePixel != null;
  1660. },
  1661. isMiOS: function () {
  1662. return this.MiOS != null;
  1663. },
  1664. isSamsung: function () {
  1665. return this.samsungOS != null;
  1666. },
  1667. isMobile: function () {
  1668. return this.android || this.ios;
  1669. },
  1670. isAndroid: function () {
  1671. return this.android;
  1672. },
  1673. isiOS: function () {
  1674. return this.ios;
  1675. },
  1676. isWeixin: function () {
  1677. return this.weixin;
  1678. }
  1679. },
  1680. Broswer: {
  1681. win: window,
  1682. nav: window.navigator,
  1683. REG_APPLE: /^Apple/,
  1684. ie: navigator.userAgent.match(/MSIE\s([\d.]+)/) || navigator.userAgent.match(/Trident\/[\d](?=[^\?]+).*rv:([0-9.].)/),
  1685. edge: navigator.userAgent.match(/Edge\/([\d.]+)/),
  1686. chrome: navigator.userAgent.match(/Chrome\/([\d.]+)/) || navigator.userAgent.match(/CriOS\/([\d.]+)/),
  1687. webview: !this.chrome && navigator.userAgent.match(/(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/),
  1688. safari: this.webview || navigator.userAgent.match(/Version\/([\d.]+)([^S](Safari)|[^M]*(Mobile)[^S]*(Safari))/),
  1689. chromiumType: null,
  1690. _getChromiumType: function () {
  1691. if (this.chromiumType != null) {
  1692. return this.chromiumType;
  1693. }
  1694. var win = this.win;
  1695. if (this.isIE() || void 0 === win.scrollMaxX || this.REG_APPLE.test(this.nav.vendor || '')) {
  1696. return '';
  1697. }
  1698. // 搜狗浏览器
  1699. if (this._testExternal(/^sogou/i, 0)) {
  1700. return 'sogou';
  1701. }
  1702. // 猎豹浏览器
  1703. if (this._testExternal(/^liebao/i, 0)) {
  1704. return 'liebao';
  1705. }
  1706. // 360浏览器
  1707. if (this.nav.mimeTypes[30] || !this.nav.mimeTypes.length) {
  1708. return '360';
  1709. }
  1710. // chrome
  1711. if (win.clientInformation && win.clientInformation.permissions) {
  1712. return 'chrome';
  1713. }
  1714. return '';
  1715. },
  1716. _testExternal: function (reg, type) {
  1717. var external = this.win.external || {};
  1718. for (var i in external) {
  1719. if (reg.test(type ? external[i] : i)) {
  1720. return true;
  1721. }
  1722. }
  1723. return false;
  1724. },
  1725. isIE: function () {
  1726. return this.ie != null;
  1727. },
  1728. ieVersion: function () {
  1729. return this.ie != null ? parseInt(this.ie[1]) : false;
  1730. },
  1731. isEdge: function () {
  1732. return this.edge != null;
  1733. },
  1734. isSafari: function () {
  1735. return this.safari != null;
  1736. },
  1737. is360: function () {
  1738. this.chromiumType = this._getChromiumType();
  1739. return this.chromiumType === '360';
  1740. },
  1741. isSogou: function () {
  1742. this.chromiumType = this._getChromiumType();
  1743. return this.chromiumType === 'sogou';
  1744. },
  1745. isChromium: function () {
  1746. return this._getChromiumType() === 'chrome'
  1747. },
  1748. webglAvailable: function () {
  1749. try {
  1750. var canvas = document.createElement('canvas');
  1751. return !!( window.WebGLRenderingContext && (
  1752. canvas.getContext('webgl') ||
  1753. canvas.getContext('experimental-webgl') )
  1754. );
  1755. } catch (e) {
  1756. return false;
  1757. }
  1758. }
  1759. },
  1760. };
  1761. var head=document.getElementsByTagName('head')[0];
  1762. head.appendChild(AVR.createTag('meta',{'name':'viewport','content':"width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0,minimal-ui,user-scalable=no"}));
  1763. head.appendChild(AVR.createTag('meta',{'name':'google','content':"notranslate"}));
  1764. if(AVR.debug) {
  1765. window.onerror = function (msg, url, l) {
  1766. var txt = "There was an error on this page.\n\n";
  1767. txt += "Error: " + msg + "\n";
  1768. txt += "URL: " + url + "\n";
  1769. txt += "Line: " + l + "\n\n";
  1770. AVR.msgBox(txt, 36, document.body);
  1771. return true;
  1772. }
  1773. }