PageSourceSearch

https://micekorea.or.kr/weven_service/asset/js/bm2/com/VR.js

js micekorea.or.kr collected 2026-10-06 09:08:07 UTC 20,866 bytes, 603 lines download raw bytes

1/**
2 * Created by kjh on 2020-07-03.
3 */
4var VR = function( container, textureImg, createComplete ){
5
6    this.mode = VR.MODE_USER_CONTROL;
7
8    this.container = container;
9    this.textureImg = textureImg;
10    this.createComplete = createComplete;
11    this.onBtnHandler = null;
12    this.onReady = null;
13    this.isBtnClicked = false;
14
15    this.latitude = -0; // - 로 갈수록 아래로 움직임.
16    this.longitude = -180; // - 로 갈수록 오른쪽으로 움직임.
17    this.canvasWidth;
18    this.canvasHeight;
19
20    this.scene;
21    this.camera;
22    this.sphere;
23    this.sphereMaterial;
24    this.raycaster;
25
26    this.btnList = [];
27    this.slider;
28    this.controls;
29    this.loader;
30    this.isLandscape;
31
32    this.currentZoomSize = 0.5;
33    this.touchZoomStatus = false;
34
35
36    var deviceInfo = {
37        alpha : 0
38        ,beta : 0
39        ,gamma : 0
40        ,orient : 1
41    }
42
43
44    var PATH = WEVEN_JS_WEB_PATH ? WEVEN_JS_WEB_PATH : "../../";
45    var _this = this;
46
47    if( VR.scriptLoaded == false ){
48        bm.scriptLoader( function(){
49            bm.scriptLoader( function(){
50                VR.scriptLoaded = true;
51                _this.init();
52            }, PATH + "libs/three/DeviceOrientationController.js" );
53        }, PATH + "libs/three/three.js" );
54    }
55
56    this.render = function (){
57        __render();
58    }
59
60    var _this = this;
61    function __render(){
62        if( _this.mode == VR.MODE_USER_CONTROL ){
63            _this.latitude = Math.max(-85, Math.min(85, _this.latitude));
64
65            var x = 50 * Math.sin(THREE.Math.degToRad(90-_this.latitude)) * Math.cos(THREE.Math.degToRad(_this.longitude));
66            var y = 50 * Math.cos(THREE.Math.degToRad(90-_this.latitude));
67            var z = 50 * Math.sin(THREE.Math.degToRad(90-_this.latitude)) * Math.sin(THREE.Math.degToRad(_this.longitude));
68
69            _this.camera.target.x = x;
70            _this.camera.target.y = y;
71            _this.camera.target.z = z;
72
73            _this.camera.lookAt(_this.camera.target);
74
75        }else{
76            requestAnimationFrame( __render );
77            _this.controls.update();
78        }
79
80        _this.renderer.render(_this.scene, _this.camera);
81    }
82}
83VR.MODE_USER_CONTROL = "MODE_USER_CONTROL";
84VR.MODE_DEVICE_ORIENTATION = "MODE_DEVICE_ORIENTATION";
85VR.scriptLoaded = false;
86
87VR.prototype = {
88
89    init : function(){
90
91        $(this.container).css( "position", "relative" );
92        // $(container).append( '<div id="vrBtnBox" style="position:absolute; right:30px; top:20px; z-index: 99999;">'
93        //     +'<div id="vrModeBtn" style=" cursor:pointer; width:110px; text-align: center; padding:7px 0; color:#ccc; border: 1px solid #666; background-color: rgba(0,0,0,0.8);">사용자 회전 모드</div>'
94        //     +'<div id="vrZoomCon" style="margin-top: 15px; width:110px; position: relative;" >'
95        //     +'<div id="vrZoomText" style="color:#ddd;">+ Zoom Control</div>'
96        //     +'<div id="vrZoomBtn" style="position:relative; cursor:pointer; width:40px; margin-left:34px; z-index: 2; height:18px; padding-top: 2px; text-align: center; top: 12px; background-color: #000; border: 1px solid #666; color:#ccc;">50%</div>'
97        //     +'<div id="vrZoomBar" style="position:absolute; width:100%; height:2px; background-color: #000;"></div>'
98        //     +'</div>'
99        //     +'</div>');
100
101        this.renderer = new THREE.WebGLRenderer();
102        this.renderer.setSize(this.container.offsetWidth, this.container.offsetHeight);
103        this.canvasWidth = this.container.offsetWidth;
104        this.canvasHeight = this.container.offsetHeight;
105        this.container.appendChild(this.renderer.domElement);
106
107        this.scene = new THREE.Scene();
108        this.camera = new THREE.PerspectiveCamera(80, this.container.offsetWidth / this.container.offsetHeight, 1, 1000);
109        this.camera.target = new THREE.Vector3(0, 0, 0);
110
111        this.sphere = new THREE.SphereGeometry(100, 100, 50);
112        this.sphere.applyMatrix(new THREE.Matrix4().makeScale(-1, 1, 1));
113        this.sphereMaterial = new THREE.MeshBasicMaterial();
114        this.raycaster = new THREE.Raycaster();
115        this.loader = new THREE.TextureLoader();
116
117        this.sphereMesh = new THREE.Mesh( this.sphere, this.sphereMaterial);
118        this.scene.add( this.sphereMesh );
119
120        this.onScreenOrientationChange();
121        this.initLoadTexture();
122        this.initSlider();
123
124        if( this.createComplete ) this.createComplete();
125
126
127
128        this.registEvent();
129        this.registMouseEventHandler();
130        this.registTouchEventHandler();
131    }
132
133    ,initLoadTexture : function () {
134        var _this = this;
135        Progress.ON();
136        this.loader.load( this.textureImg, function(tex){
137            Progress.OFF();
138            _this.sphereMaterial.map = tex;
139
140            if( bm.isMobile ){
141                _this.controls = new DeviceOrientationController( _this.camera, _this.renderer.domElement );
142                events( _this.controls ).addListener( "ZOOM_CHANGE", function(e){
143                    _this.setZoom( _this.currentZoomSize + e.data.changeZoomSize, true );
144                })
145                _this.changeMode(VR.MODE_DEVICE_ORIENTATION );
146            }else{
147                _this.changeMode(VR.MODE_USER_CONTROL);
148            }
149
150            if( _this.onReady ) _this.onReady();
151        } );
152    }
153
154    ,initSlider : function () {
155        var _this = this;
156        this.slider  = new BMSlider( "fill" );
157        this.slider.init( $("#vrZoomBtnCon"), "#vrZoomBtn", "#vrZoomBar");
158        this.slider.onChangeHandler = function(per){
159            _this.zoomControl( per );
160        }
161    }
162    
163    ,registEvent : function () {
164        var _this = this;
165
166        window.addEventListener('orientationchange', function(e){
167           _this.onScreenOrientationChange();
168        });
169
170        $(this.container).on("mousewheel", function(e){
171            var delta, per;
172
173            if( e.originalEvent ) e = e.originalEvent;
174            if (e.wheelDelta) delta = e.wheelDelta / 120; /* IE/Chrome/Opera */
175            else if (e.detail) delta = -e.detail/3; /* Mozilla case */
176
177            if( delta > 0 ){
178                _this.setZoom( _this.currentZoomSize + 0.1 );
179            }else{
180                _this.setZoom( _this.currentZoomSize - 0.1 );
181            }
182        });
183        if( $("#vrModeBtn").length > 0 ){
184            mouseAdapter( $("#vrModeBtn")[0]).onDown( function(e){
185                e.preventDefault();
186                e.stopImmediatePropagation();
187                if( bm.isMobile == false ) {
188                    alert( "PC에서는 지원하지 않습니다." );
189                    return;
190                }
191                if( _this.mode == VR.MODE_USER_CONTROL ){
192                    _this.changeMode( VR.MODE_DEVICE_ORIENTATION );
193                }else{
194                    _this.changeMode( VR.MODE_USER_CONTROL );
195                }
196            })
197        }
198    }
199
200    ,registTouchEventHandler : function () {
201
202        var _this = this;
203        var startZoomSize;
204        var startDistance;
205        var currentDistance;
206        var touchZoomPower = 0.004;
207
208        document.addEventListener("touchstart", onTouchStart );
209        document.addEventListener("touchmove", onTouchMove );
210        document.addEventListener("touchend", onTouchEnd );
211
212        function onTouchStart(e){
213            if( _this.mode != VR.MODE_DEVICE_ORIENTATION &&  e.touches.length == 2 ){
214
215                e.preventDefault();
216                e.stopImmediatePropagation();
217                _this.touchZoomStatus = true;
218                startDistance = Math.sqrt( Math.pow( (e.touches[1].clientX - e.touches[0].clientX), 2 ) +  Math.pow( ( e.touches[1].clientY - e.touches[0].clientY ),2) );
219                startZoomSize = _this.currentZoomSize;
220            }
221        }
222
223        function onTouchMove(e) {
224            if( _this.mode != VR.MODE_DEVICE_ORIENTATION && e.touches.length == 2 ){
225
226                e.preventDefault();
227                e.stopImmediatePropagation();
228
229                _this.touchZoomStatus = true;
230                currentDistance = Math.sqrt( Math.pow( (e.touches[1].clientX - e.touches[0].clientX), 2 ) +  Math.pow( ( e.touches[1].clientY - e.touches[0].clientY ),2) );
231                _this.setZoom( startZoomSize + ( currentDistance - startDistance ) * touchZoomPower );
232            }
233        }
234
235        function onTouchEnd(e) {
236            if ( _this.mode != VR.MODE_DEVICE_ORIENTATION && e.touches.length == 2) {
237                e.preventDefault();
238                e.stopImmediatePropagation();
239            }
240
241            _this.touchZoomStatus = false;
242        }
243    }
244
245    ,registMouseEventHandler : function () {
246
247        var targetDownBtn = null;
248        var touchSensorSpeed = 0.3; // 0.01 ~ 0.5 정도 사이 추천함.
249        var sensorSpeed = 0.1; // 0.01 ~ 0.5 정도 사이 추천함.
250        var savedX;
251        var savedY;
252        var _this = this;
253
254        mouseAdapter(this.container).onDrag(onMove, onUp, onDown);
255        window.addEventListener("mousemove", onMouseMoveHandlerForCurosr );
256
257        function onDown( e ){
258
259            if( _this.touchZoomStatus ) return;
260
261            var containerX, containerY;
262            containerX = $(_this.container).offset().left;
263            containerY = $(_this.container).offset().top;
264
265            var vector = new THREE.Vector3(
266                ( (e.clientX - containerX)/ _this.canvasWidth ) * 2 - 1,
267                - ( (e.clientY - containerY) / _this.canvasHeight ) * 2 + 1, 0.5 );
268            vector.unproject( _this.camera );
269            _this.raycaster.set( _this.camera.position, vector.sub( _this.camera.position ).normalize() );
270
271            // 오브제가 마우스 이벤트의 타겟으로 존재하는지 확인.
272            for( var i = 0; i<_this.btnList.length; i++){
273                var intersects = _this.raycaster.intersectObject( _this.btnList[i] );
274                if ( intersects.length > 0 ) {
275                    targetDownBtn = _this.btnList[i];
276                    _this.isBtnClicked = true;
277                }
278            }
279
280            // 타겟으로 존재하지 않을때만 버튼 클릭상태를 false로 변경하고,
281            if( !targetDownBtn ){
282                _this.isBtnClicked = false;
283            }
284
285            if( _this.mode != VR.MODE_USER_CONTROL ) return;
286
287            e.preventDefault();
288
289            savedX = e.clientX;
290            savedY = e.clientY;
291
292        }
293
294        function onMove( e ){
295            if( _this.touchZoomStatus ) return;
296            if( _this.mode != VR.MODE_USER_CONTROL ) return;
297
298            _this.isBtnClicked = false;
299
300            if( bm.isMobile ){
301                _this.longitude = (savedX - e.clientX) * touchSensorSpeed + _this.longitude;
302                _this.latitude = (e.clientY - savedY) * touchSensorSpeed + _this.latitude;
303            }else{
304                _this.longitude = (savedX - e.clientX) * sensorSpeed + _this.longitude;
305                _this.latitude = (e.clientY - savedY) * sensorSpeed + _this.latitude;
306            }
307
308            _this.render(1);
309
310            savedX = e.clientX;
311            savedY = e.clientY;
312        }
313
314
315        function onUp( e ){
316
317            if( _this.touchZoomStatus ) return;
318
319            if( targetDownBtn ){
320                var containerX, containerY;
321                containerX = $(_this.container).offset().left;
322                containerY = $(_this.container).offset().top;
323
324                var vector = new THREE.Vector3(
325                    ( (e.clientX - containerX)/ _this.canvasWidth ) * 2 - 1,
326                    - ( (e.clientY - containerY) / _this.canvasHeight ) * 2 + 1, 0.5 );
327                vector.unproject( _this.camera );
328                _this.raycaster.set( _this.camera.position, vector.sub( _this.camera.position ).normalize() );
329
330
331                for( var i = 0; i<_this.btnList.length; i++){
332                    var intersects = _this.raycaster.intersectObject( _this.btnList[i] );
333
334                    if ( intersects.length > 0 && targetDownBtn == _this.btnList[i] ) {
335                        e.stopImmediatePropagation();
336                        if( _this.onBtnHandler ) _this.onBtnHandler( _this.btnList[i] );
337                        _this.isBtnClicked = false;
338                        return;
339                    }
340                }
341
342                targetDownBtn = null;
343            }
344
345            _this.isBtnClicked = false;
346            if( _this.mode != VR.MODE_USER_CONTROL ) return;
347            requestAnimationFrame( _this.render );
348        }
349
350        function onMouseMoveHandlerForCurosr(e){
351
352            var mouse = new THREE.Vector2();
353            mouse.x = ( e.clientX / window.innerWidth ) * 2 - 1;
354            mouse.y = - ( e.clientY / window.innerHeight ) * 2 + 1;
355
356            _this.raycaster.setFromCamera( mouse, _this.camera );
357
358            for( var i = 0; i<_this.btnList.length; i++){
359                var intersects = _this.raycaster.intersectObject( _this.btnList[i] );
360                if ( intersects.length > 0 ) {
361                    $("body").addClass("pointer" );
362                    break;
363                }else{
364                    $("body").removeClass("pointer" );
365                }
366            }
367        }
368
369    }
370
371    ,changeImage : function( src ){
372
373        var _this = this;
374        Progress.ON();
375        loader.load( src, function(tex) {
376            Progress.OFF();
377            _this.sphereMaterial.map = tex;
378            _this.render();
379        });
380    }
381
382
383    ,onScreenOrientationChange : function(e){
384        this.isLandscape = document.documentElement.clientHeight < document.documentElement.clientWidth;
385    }
386
387
388    ,changeSize : function( width, height, realWidth, realHeight ){
389
390        var canvas = $(this.container).find("canvas");
391        canvas.attr("width", width+"px").attr("height", height+"px").css({width:width, height:height});
392
393        this.canvasWidth = width;
394        this.canvasHeight = height;
395
396        realWidth = realWidth || width;
397        realHeight = realHeight || height;
398
399        this.renderer.setViewport(0, 0, realWidth, realHeight);
400        this.camera.aspect = realWidth / realHeight;
401        this.camera.updateProjectionMatrix();
402
403        this.render();
404
405        this.zoomControl(this.currentZoomSize);
406    }
407
408    /**
409     * 수동 <=> 자동(디바이스 기울기로) 회전모드 변환
410     * @param changeMode
411     */
412    ,changeMode : function( changeMode ){
413
414        if( this.mode == changeMode ){
415            this.render();
416            return;
417        }
418
419        this.mode = changeMode;
420        if( this.mode == VR.MODE_USER_CONTROL ){
421            $(this.container).find("#vrModeBtn").text( "수동 회전 모드" );
422            if( this.controls ){
423                this.controls.disconnect();
424            }
425        }else{
426            $(this.container).find("#vrModeBtn").text( "디바이스 회전 모드" );
427            this.controls.connect();
428        }
429
430        this.render();
431    }
432
433    ,setZoom : function( per, onlyChangeValue ){
434        per = this.zoomControl( per, onlyChangeValue );
435        this.slider.setValue( per );
436    }
437
438
439    ,zoomControl : function( per, onlyChangeValue ){
440
441        if( per > 1 ) per = 1;
442        else if( per < 0 ) per = 0;
443
444        this.currentZoomSize = per;
445        $("#vrZoomSize").text( Math.floor( per * 100 ) + "%" );
446
447        if( $(window).width() > $(window).height() ){
448            this.camera.fov = 55 - 20*per;
449        }else{
450            this.camera.fov = 100 - 60*per;
451        }
452
453        this.camera.updateProjectionMatrix();
454
455        if( !onlyChangeValue ) this.render();
456
457        return per;
458    }
459
460    /**
461     data = {
462          imgUrl : ""
463          ,x : 0
464          ,y : 0
465          ,z : -40
466          ,rotationX : 0
467          ,rotationY : 0
468          ,width : 10
469          ,height : 10
470          ,opacity : 1
471     }
472     * @param data
473     */
474    ,insertImg : function( data ){
475
476        data.opacity = data.opacity === undefined ? 1 : data.opacity;
477        var textureLoader = new THREE.TextureLoader();
478        var img = new THREE.MeshBasicMaterial({ //CHANGED to MeshBasicMaterial
479            map : textureLoader.load( data.imgUrl )
480            ,opacity: data.opacity
481            ,transparent: data.opacity === 1 ? false : true
482        });
483        img.map.needsUpdate = true; //ADDED
484        data.width = data.width || 3;
485        data.height = data.height || 3;
486        data.depth = data.depth || 1;
487
488        var plane = new THREE.Mesh(new THREE.PlaneGeometry(data.width, data.height, data.depth, 1, 0, 0),img);
489        plane.position.x = data.x;
490        plane.position.y = data.y;
491        plane.position.z = data.z;
492
493        plane.rotation.x = data.rotationX * Math.PI / 180;
494        plane.rotation.y = data.rotationY * Math.PI / 180;
495
496        this.scene.add(plane);
497
498        this.btnList.push( plane );
499        return plane;
500    }
501
502
503    // ,quaternionTween : function(deg, vector, camera, time, callback){
504    //     let newQuaternion = new THREE.Quaternion();
505    //     var slerpTarget  = new THREE.Quaternion().setFromAxisAngle( vector, that.getRadians(deg) );
506    //
507    //     THREE.Quaternion.slerp( sceneCamera.quaternion, slerpTarget, newQuaternion, time );
508    //     sceneCamera.applyQuaternion(newQuaternion);
509    // }
510
511
512    ,getLatLongByXYZ : function (pointX, pointY, pointZ) {
513        var pointOnSphere = new THREE.Vector3();
514        pointOnSphere.x = pointX;
515        pointOnSphere.y = pointY;
516        pointOnSphere.z = pointZ;
517
518        var EarthRadius = 20;
519        var lat = 90 - (Math.acos(pointOnSphere.y / EarthRadius) * 180) / Math.PI;
520        var lon = ((90 + (Math.atan2(pointOnSphere.x, pointOnSphere.z) * 180) / Math.PI) % 360) - 180;
521        let latAndLongObj = { latitude: lat, longitude: lon };
522        return latAndLongObj;
523    }
524
525    /**
526     * converts a XYZ THREE.Vector3 to longitude latitude. beware, the vector3 will be normalized!
527     * @param vector3
528     * @returns an array containing the longitude [0] & the lattitude [1] of the Vector3
529     */
530    ,vector3toLonLat :function ( vector3 )
531    {
532        vector3.normalize();
533        var lng = - Math.atan2( -vector3.z, -vector3.x ) - Math.PI;
534        if( lng < - Math.PI )lng += Math.PI * 2;
535        if( lng > Math.PI )lng -= Math.PI * 2;
536
537        var p = new THREE.Vector3( vector3.x, 0, vector3.z );
538        p.normalize();
539
540        var lat = Math.acos( p.dot( vector3 ) );
541        if( vector3.y < 0 ) lat *= -1;
542
543        return [ lng,lat ];
544
545    }
546
547    /**
548     * locateCamera: orients a camera to look at another object & preserve the camera's UP axis
549     * @param target object to lookAt
550     * @param camera object (camera) to position
551     * @param camera_angle extra angle on the latitude
552     * @param camera_distance distance between the target and the camera
553     */
554    ,locateCamera : function ( target, camera, camera_angle, camera_distance )
555    {
556
557        var UP = new THREE.Vector3( 0,1,0 );
558        var NORMAL = target.clone().normalize();
559
560        var angle = Math.acos( UP.dot( NORMAL ) );
561        angle += camera_angle || 0;
562
563        if( angle > Math.PI )    UP.y *= -1;
564        if( angle < 0 )          angle += Math.PI;
565
566        var AX = UP.crossVectors( UP, NORMAL );
567
568        var tmp = new THREE.Vector3( 0,1,0 );
569        tmp.applyAxisAngle( AX, angle );
570        tmp.multiplyScalar( camera_distance ).add( target );
571
572        camera.position.copy( tmp );
573        camera.lookAt( target );
574    }
575
576
577    ,getCoordinatesFromLatLng : function(latitude, longitude, radiusEarth)
578    {
579        let latitude_rad = latitude * Math.PI / 180;
580        let longitude_rad = longitude * Math.PI / 180;
581
582        let xPos= radiusEarth * Math.cos(latitude_rad) * Math.cos(longitude_rad);
583        let zPos = radiusEarth * Math.cos(latitude_rad) * Math.sin(longitude_rad);
584        let yPos = radiusEarth * Math.sin(latitude_rad);
585
586        return {x: xPos, y: yPos, z: zPos};
587    }
588
589    , latLongToVector3: function(lat, lon, radius, heigth) {
590        var phi = (lat)*Math.PI/180;
591        var theta = (lon-180)*Math.PI/180;
592
593        var x = -(radius+heigth) * Math.cos(phi) * Math.cos(theta);
594        var y = (radius+heigth) * Math.sin(phi);
595        var z = (radius+heigth) * Math.cos(phi) * Math.sin(theta);
596
597        return new THREE.Vector3(x,y,z);
598    }
599
600}
601
602
603

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.