Panolens compass sensor

Compass sensor

지난번에 구현한 compass 컴퓨터에서는 잘 작동하는데 Control을 Sensor로 설정을 하니까 역시나 동작하지 않았다.

Sensor 선택 시 어떻게 동작을 하는지 구조부터 파악 하는 것이 먼저였다. Sensor로 검색을 하면 enableControl랑 연결되어 있고 여기서 index를 받아서 this.controls에서 control을 가져온다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
 * Enable control by index
 * @param  {PANOLENS.Controls} index - Index of camera control
 */
PANOLENS.Viewer.prototype.enableControl = function ( index ) {
 
    index = ( index >= 0 && index < this.controls.length ) ? index : 0;
 
    this.control.enabled = false;
 
    this.control = this.controls[ index ];
 
    this.control.enabled = true;
 
    switch ( index ) {
 
        case PANOLENS.Controls.ORBIT:
 
            this.camera.position.copy( this.panorama.position );
            this.camera.position.z += 1;
 
            break;
 
        case PANOLENS.Controls.DEVICEORIENTATION:
 
            this.camera.position.copy( this.panorama.position );
 
            break;
 
        default:
 
            break;
    }
 
    this.control.update();
 
    this.activateWidgetItem( index, undefined );
 
};
cs

this.controls

다시 this.controls로 검색하면 PANOLENS.Viewer의 멤버변수로 찾을 수 있다. this.controls는 this.OrbitControls, this.DeviceOrientationControls를 가지는 배열이다.

1
2
// Controls
this.controls = [ this.OrbitControls, this.DeviceOrientationControls ];
cs

여기서 this.DeviceOrientationControls가 스마튼폰, Sensor와 밀접해 보였다. this.DeviceOrientationControls는 three.js 의 new THREE.DeviceOrientationControls( this.camera, this.container);으로 생성하고 있었는데 파라미터로 카메라와 컨테이너를 메개변수로 받아서 생성하고 있었다.

1
this.DeviceOrientationControls = new THREE.DeviceOrientationControls( this.camera, this.container, this );
cs

new THREE.DeviceOrientationControls

다시 DeviceOrientationControls을 검색해서 코드를 보니 여기서 디바이스의 센서값을 처리하는 부분인지 알 수 있었다. 디바이스의 센서값을 계속 가져와서 카메라를 이동시키는 작업을 하니까 this.update 함수부터 살펴보았다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
this.update = function( ignoreUpdate ) {
 
    if ( scope.enabled === false )    
        return;
    
 
    var alpha = scope.deviceOrientation.alpha ? THREE.Math.degToRad( scope.deviceOrientation.alpha ) + this.alphaOffsetAngle : 0// Z
    var beta = scope.deviceOrientation.beta ? THREE.Math.degToRad( scope.deviceOrientation.beta ) : 0// X'
    var gamma = scope.deviceOrientation.gamma ? THREE.Math.degToRad( scope.deviceOrientation.gamma ) : 0// Y''
    var orient = scope.screenOrientation ? THREE.Math.degToRad( scope.screenOrientation ) : 0// O
 
    setCameraQuaternion( scope.camera.quaternion, alpha, beta, gamma, orient );
    
    this.alpha = alpha;
 
    ignoreUpdate !== true && this.dispatchEvent( changeEvent );
};
cs

무언갈 뚝딱뚝딱 만들어 setCameraQuaternion을 호출하네?

setCameraQuaternion

사실 여기서 어떤 동작을 하는지 잘 모르겠다. 일단은 Control에서 Sensor 선택하면 이부분이 계속호출되고 quaternion 도 관련 있으니 여기서 Compass를 업데이트 해주는 부분을 넣으면 동작할 것 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*
 * 작성자: silqwer
 * 설명: 나침판 회전 함수
 */
PANOLENS.Viewer.prototype.onRotationChange = function (element){
    
    var camera = this.camera;
    
    this.control.addEventListener('change'function(){
        
        var rotationY = camera.rotation.y;
        var dy = 0
        
        if ( rotationY < 0 ){
            dy = THREE.Math.radToDeg( rotationY + ( 2 * Math.PI ) );
        }else{
            dy = THREE.Math.radToDeg( rotationY );
        }
        
        dy = Math.round(dy) * -1;
        element.style.transform = 'rotate('+ (dy % 360+'deg)';
    }, false);
    
};
cs

앞서 만든 onRotationChange 함수의 경우 카메라와 나침판 바늘역활을 하는 element가 필요하다. 두녀석을 어디서 가져올까 고민을 하다가 Panolens에서 DeviceOrientationControls을 선언하는 부분에서 추가로 파라미터를 가져와서 사용하기로 했다.

DeviceOrientationControls 수정

1
this.DeviceOrientationControls = new THREE.DeviceOrientationControls( this.camera, this.container, this );
cs

생성할 때 this를 파라미터르 추가해 Viewer를 가져온다. 그리곤 setCameraQuaternion가 호출될 때마다 compass를 업데이트 시켜줄 onDeviceMoveEvent 함수를 하나 만들고 setCameraQuaternion에 추가한다.

onDeviceMoveEvent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*
 * 작성자: silqwer
 * 설명: Sensor 모드 시 Compass 동작 함수 
 */
var onDeviceMoveEvent = function (panolensViewer) {
    var rotationY = panolensViewer.camera.rotation.y;
    
    var dy = 0
    var element;
    if ( rotationY < 0 ){
        dy = THREE.Math.radToDeg( rotationY + ( 2 * Math.PI ) );
    }else{
        dy = THREE.Math.radToDeg( rotationY );
    }
 
    dy = Math.round(dy) * -1;
    
    element = panolensViewer.widget.compassElement.childNodes[2];
    element.style.transform = 'rotate('+ (dy % 360+'deg)';
};
cs

결과

c4