Ketika mengembangkan proyek saya pada topik ruang, saya menemukan fakta bahwa untuk beberapa alasan three.js tidak memiliki alat kontrol kamera yang siap pakai dan nyaman yang cocok untuk tugas-tugas tersebut. Tentu saja saya akui bahwa saya hanya kelihatan buruk ... Tapi, pencarian yang cukup panjang untuk hasil tidak menghasilkan.
OrbitControls adalah favorit tradisional dari tiga contoh. Js, ia tidak tahu cara membalikkan kamera, dan ia tidak tahu berapa banyak hal lain yang dibutuhkannya.
TrackballControls luar biasa karena kamera berputar di sekitar objek sesuka Anda, dan terbalik juga, tetapi tidak tahu cara menghidupkan sumbu tampilan, tidak tahu cara bergerak bolak-balik tanpa mengubah skala, tidak ada penyesuaian nyaman kecepatan gerakan dan belokan.
FlyControls - sebaliknya, memungkinkan Anda untuk membuat "tong" dan mengubah kecepatan, tapi ... kemana
perginya rotasi kamera di sekitar objek yang dimaksud?
Tentu saja mungkin untuk keluar dengan bantuan semua jenis kruk, tetapi entah bagaimana ini tidak aman. Setelah memastikan bahwa tidak ada solusi siap pakai untuk tujuan saya, saya memutuskan untuk membuatnya sendiri. Siapa yang tertarik, silakan, di bawah kucing.
Saya memutuskan untuk mengambil TrackballControls.js sebagai dasar, setelah membukanya, kami melihat penulis:
Ini adalah pekerjaan mereka dan akan menjadi titik awal kami. Perlu menambahkan sedikit, dan jika mungkin tidak merusak yang sudah ada. (Namun, sesuatu harus dihentikan).
Untuk mulai dengan, apa yang kita miliki: kamera berputar di sekitar objek tanpa batasan, tidak terjebak pada tiang, sama sekali tidak mempertahankan kecepatan sudut konstan. Ada zoom, ada wajan. Ini bagus, tetapi tidak cukup.
Kejutannya adalah posisi aneh dengan tombol. Sebenarnya, ini tidak terduga:
this.keys = [ 65 , 83 , 68 ];
Kontrol kamera hanya dengan tiga tombol? mungkin ini ide yang bagus, tetapi nyatanya itu tidak berhasil. Di pengendali event keydown di sumber, kami mengamati baris berikut:
window.removeEventListener( 'keydown', keydown ); _prevState = _state; if ( _state !== STATE.NONE ) { return; } else if ( event.keyCode === _this.keys[ STATE.ROTATE ] && ! _this.noRotate ) { _state = STATE.ROTATE; } else if ( event.keyCode === _this.keys[ STATE.ZOOM ] && ! _this.noZoom ) { _state = STATE.ZOOM; } else if ( event.keyCode === _this.keys[ STATE.PAN ] && ! _this.noPan ) { _state = STATE.PAN; }
Terlihat sedikit misterius, tapi ... tidak berhasil. Yaitu secara umum. Tombol-tombol di atas tidak melakukan apa-apa pada kenyataannya, kamera hanya dikendalikan oleh mouse atau sentuhan.
Saya mengomentari bagian kode ini sebagai tidak perlu, dan menggantinya dengan kode saya, yang kisahnya akan sedikit lebih jauh. Menghapus panggilan balik untuk pemrosesan tombol - jelas, perlu bahwa tombol hanya berfungsi sekali setiap kali mereka ditekan. Tapi ini bukan perilaku yang dibutuhkan di luar angkasa, jadi itu juga di bawah komentar.
Jadi, apa yang kita lewatkan di sini? Apa yang saya putuskan untuk ditambahkan:
- Gerakan bolak-balik di sepanjang garis pandang.
- Rotasi kamera yang porosnya adalah garis pandang.
- Autorotation.
- Kemampuan untuk mengubah kecepatan semua gerakan dan belok dengan satu tombol.
- Kontrol keyboard penuh.
Saya harap pembaca akan memaafkan saya untuk fakta bahwa saya akan berbicara tentang proses pengeditan tidak dalam urutan menyelesaikan subtugas ini, tetapi dalam urutan mengikuti kode sumber dari atas ke bawah. Saya akan menjelaskan apa dan mengapa saya lakukan.
Konstruktor
Tambahkan beberapa variabel:
this.rotationZFactor = 0.0; this.RVMovingFactor = 0.0; this.autoRotate = false; this.autoRotateSpeed = 0.001; this.allSpeedsFactor = 1;
rotasiZFaktor diperlukan untuk mengontrol rotasi kamera pada poros pandang,
RVMovingFactor untuk mengontrol maju dan mundur di sepanjang sumbu yang sama,
autoRotate Saya kira itu tidak perlu komentar,
allSpeedsFactor untuk mengontrol kecepatan semua gerakan, gabungan, dan belokan.
Metode RotateCamera
Diorganisasikan dengan cara ini:
this.rotateCamera = ( function () { var axis = new THREE.Vector3(), quaternion = new THREE.Quaternion(), eyeDirection = new THREE.Vector3(), objectUpDirection = new THREE.Vector3(), objectSidewaysDirection = new THREE.Vector3(), moveDirection = new THREE.Vector3(), tmpQuaternion = new THREE.Quaternion(), angle; return function rotateCamera() { ...
Di sini, hampir semuanya begitu, satu-satunya yang saya tambahkan adalah variabel tmpQuaternion untuk memproses rotasi kamera di sepanjang sumbu Z.
Lebih jauh, memeriksa apakah ada pemrosesan yang diperlukan cukup mudah:
moveDirection.set( _moveCurr.x - _movePrev.x, _moveCurr.y - _movePrev.y, 0 ); angle = moveDirection.length(); if ( angle ) {
Dan menjadi seperti ini:
if (_this.autoRotate) _moveCurr.x += _this.autoRotateSpeed; moveDirection.set( _moveCurr.x - _movePrev.x, _moveCurr.y - _movePrev.y, 0 ); moveDirection.setLength(moveDirection.length() * _this.allSpeedsFactor); angle = moveDirection.length(); if ( angle || _this.rotationZFactor) {
Apa yang ada di sini dan mengapa. Pertama, jika rotasi otomatis aktif, maka kami menambah kecepatannya ke rotasi di sepanjang sumbu X. Kedua, kami menambahkan penskalaan semua rotasi (Dan rotasi otomatis juga) ke semuaSpeedFactor. Untuk mengontrol kecepatan semua konversi. Dan ketiga, kami akan melakukan tindakan lebih lanjut tidak hanya dalam kasus ketika sudut sudut (pergerakan kamera relatif ke controls.target) tidak nol, tetapi juga dalam kasus ketika rotasiZFactor, yang bertanggung jawab untuk rotasi kamera sepanjang sumbu Z, adalah tidak nol.
Lebih lanjut dalam kode metode, setelah menghitung angka empat rotasi, tambahan kecil:
quaternion.setFromAxisAngle( axis, angle );
Angka empat rotasi kamera di sekitar objek, kita kalikan dengan angka empat rotasi yang dihitung secara terpisah di sepanjang sumbu Z. Sekarang kamera dapat memutar sepanjang ketiga sumbu.
Metode Zoom Kamera
Saya akan memberi Anda seluruh kode, ini kecil:
this.zoomCamera = function () { var factor; if ( _state === STATE.TOUCH_ZOOM_PAN ) {
Semua perubahan dilakukan untuk menambahkan allSpeedsFactor di sejumlah tempat untuk mengontrol kecepatan (gerakan, rotasi) dan zoom.
Metode PanCamera
Saya juga akan memberikan seluruh kode, sebagai itu kecil:
this.panCamera = ( function () { var mouseChange = new THREE.Vector2(), objectUp = new THREE.Vector3(), rv = new THREE.Vector3() pan = new THREE.Vector3(); return function panCamera() { mouseChange.copy( _panEnd ).sub( _panStart ); mouseChange.setLength(mouseChange.length() * _this.allSpeedsFactor); if ( mouseChange.lengthSq() || _this.RVMovingFactor ) { mouseChange.multiplyScalar( _eye.length() * _this.panSpeed ); pan.copy( _eye ).cross( _this.object.up ).setLength( mouseChange.x ); pan.add( objectUp.copy( _this.object.up ).setLength( mouseChange.y ) ); pan.add( rv.copy( _eye ).setLength(_this.RVMovingFactor * _eye.length()) ); _this.object.position.add( pan ); _this.target.add( pan ); if ( _this.staticMoving ) { _panStart.copy( _panEnd ); } else { _panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( _this.dynamicDampingFactor * _this.allSpeedsFactor ) ); } } }; }() );
Tetapi ada lebih banyak perubahan. Saya akan memberi tahu Anda tentang semuanya secara berurutan.
Variabel rv ditambahkan - vektor untuk memproses kecepatan radial kamera, mis. kecepatannya di sepanjang sumbu z.
Menambahkan penskalaan vektor mouseChange ke allSpeedsFactor yang sama, tapi sekali lagi di sini, saya ingin menyesuaikan semua transformasi menjadi satu yang menggunakannya, sehingga akan ditambahkan untuk menangani semua gerakan.
Memeriksa kebutuhan untuk memproses sekarang lewat tidak hanya jika mouse digerakkan, tetapi juga dalam kasus ketika RVMovingFactor bukan nol - variabel yang bertanggung jawab untuk pergerakan kamera di sepanjang garis pandang.
Dan akhirnya, dalam kode metode, tambahkan vektor pan dan rv untuk mendapatkan gerakan penuh kamera di sepanjang ketiga sumbu dan menerapkannya ke kamera dan ke controls.target.
Perbarui metode
Untuk menghilangkan beberapa efek yang tidak diinginkan, baris berikut ditambahkan ke bagian paling akhir dari metode ini:
_this.RVMovingFactor = 0.0; _this.rotationZFactor = 0.0;
Efek yang tidak diinginkan dikaitkan dengan rotasi dan gerakan di sepanjang sumbu Z, seperti yang Anda duga.
Metode keydown
Kode sumber yang saya tunjukkan di awal cerita sepenuhnya dikomentari. Dan di sampingnya, praktis tidak ada apa pun dalam tubuh metode ini, sehingga kita dapat mengatakan bahwa itu ditulis ulang dari awal.
function keydown( event ) { if ( _this.enabled === false ) return; switch (event.keyCode) {
Jadi apa yang telah dilakukan di sini:
Banyak tombol mensimulasikan perubahan yang dilakukan oleh gerakan mouse.
"A", "D" - menduplikasi rotasi kamera ke kiri dan kanan.
"Z", "X" - menduplikasi kamera naik dan turun.
"+", "-" - duplikat zoom dengan roda mouse.
Tombol panah menduplikasi pergerakan kamera ke atas dan ke bawah dan ke kiri dan ke kanan, tanpa mengubah zoom dan sudut rotasi.
Dan juga menambahkan fitur di mana tombol-tombol mouse standar tidak akan cukup:
"W", "S" - gerakan di sepanjang garis pandang tanpa mengubah zoom.
"Q", "E" - rotasi kamera di sekitar sumbu Z, mis. di sekitar garis pandang.
"R", "Shift" - mengurangi kecepatan semua gerakan, belokan, transformasi, masing-masing 3 kali dan 20 kali.
Dalam hal ini, Anda dapat melihat bahwa ada penolakan untuk menggunakan array kunci. Untuk tujuan saya, ini tidak diperlukan, dan jujur, untuk mengacaukan apa yang secara pribadi tidak saya butuhkan, saya terlalu malas.
Metode keyup
Saya akan mengutipnya secara keseluruhan:
function keyup( event ) { if ( _this.enabled === false ) return; switch (event.keyCode) {
Seperti yang Anda tebak, semua yang dilakukan adalah mengembalikan nilai variabel RVMovingFactor, rotasiZFactor, allSpeedsFactor, ke nilai defaultnya.
Selain itu, mengomentari instalasi keystroke handler - karena penghapusannya dihapus dalam metode keydown.
Akhirnya, agar tidak membingungkan perpustakaan yang dimodifikasi dengan yang standar, kami mengubah namanya.
Ganti nama konstruktor:
THREE.AstroControls = function ( object, domElement ) {
Alih-alih TrackballControls, sekarang AstroControls.
Dan di akhir file kita menulis:
THREE.AstroControls.prototype = Object.create( THREE.EventDispatcher.prototype ); THREE.AstroControls.prototype.constructor = THREE.AstroControls;
Semuanya, kontrol baru siap digunakan.
Semua kodenya berada di bawah spoiler THREE.AstroControls = function ( object, domElement ) { var _this = this; var STATE = { NONE: - 1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM_PAN: 4 }; this.object = object; this.domElement = ( domElement !== undefined ) ? domElement : document; this.rotationZFactor = 0.0;
Aplikasi
Simpan kode lengkap ke file, beri nama AstroControls.js, tambahkan ke proyek Anda.
Buat kontrol seperti ini:
var controls; ...
Di masa depan, semuanya standar, tetapkan controls.target dan controls.autoRotate seperti biasa.
(Jika Anda memiliki pertanyaan, lihat contoh three.js , ada hampir di mana-mana contoh menggunakan OrbitControls. AstroControls dapat ditangani dengan cara yang sama) Dan gunakan kemampuan untuk memutar dan memindahkan kamera sesuai keinginan Anda pada ketiga sumbu, menggunakan mouse atau keyboard, atau acara sentuh.Saya harap ini bermanfaat bagi seseorang.