Bei der Entwicklung meines Projekts zum Thema Weltraum bin ich auf die Tatsache gestoßen, dass three.js aus irgendeinem Grund kein fertiges und praktisches Kamerasteuerungswerkzeug hat, das für solche Aufgaben geeignet ist. Natürlich gebe ich zu, dass ich nur schlecht ausgesehen habe ... Aber eine ziemlich lange Suche nach den Ergebnissen hat nicht ergeben.
OrbitControls ist ein traditioneller Favorit unter den Beispielen von three.js. Es weiß nicht, wie die Kamera auf den Kopf gestellt werden soll, und es weiß nicht, wie viele andere Dinge es benötigt.
TrackballControls ist
insofern bemerkenswert, als sich die Kamera nach Belieben um das Objekt dreht und auch auf dem Kopf steht. Sie weiß jedoch nicht, wie sie sich um die Sichtachse drehen soll. Sie kann sich nicht hin und her bewegen, ohne die Skala zu ändern. Es gibt keine bequeme Einstellung der Geschwindigkeit von Bewegungen und Drehungen.
FlyControls - im Gegenteil, Sie können einen „Lauf“
erstellen und die Geschwindigkeit ändern, aber ... wohin ging die Drehung der Kamera um das betreffende Objekt?
Es war natürlich möglich, mit Hilfe aller Arten von Krücken auszusteigen, aber irgendwie ist das nicht comme il faut. Nachdem ich sichergestellt hatte, dass es keine fertige Lösung für meine Zwecke gibt, habe ich beschlossen, sie selbst zu erstellen. Wer interessiert sich bitte unter Katze.
Ich habe mich für TrackballControls.js entschieden. Nachdem wir es geöffnet haben, sehen wir die Autoren:
Dies ist ihre Arbeit und wird als Ausgangspunkt dienen. Es ist notwendig, ein wenig hinzuzufügen und wenn möglich das bestehende nicht zu brechen. (Allerdings musste etwas herausgeschnitten werden).
Zunächst einmal, was wir haben: Die Kamera dreht sich uneingeschränkt um das Objekt, bleibt nicht an der Stange hängen, behält bei allen Drehungen eine konstante Winkelgeschwindigkeit bei. Es gibt einen Zoom, es gibt eine Pfanne. Das ist gut, aber nicht genug.
Die Überraschung war die seltsame Position mit den Knöpfen. In der Tat ist dies unerwartet:
this.keys = [ 65 , 83 , 68 ];
Kamerasteuerung mit nur drei Tasten? Vielleicht ist das eine gute Idee, aber tatsächlich funktioniert es nicht. Im Keydown-Ereignishandler in der Quelle beachten wir die folgenden Zeilen:
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; }
Es sieht ein wenig mysteriös aus, aber ... funktioniert nicht. Das heißt, im Allgemeinen. Die oben genannten Tasten bewirken nichts, die Kamera wird nur mit der Maus oder durch Berühren gesteuert.
Ich habe diesen Code als unnötig kommentiert und durch meinen ersetzt, worüber die Geschichte etwas weiter gehen wird. Entfernen des Rückrufs für die Tastenverarbeitung - offensichtlich war es erforderlich, dass die Tasten bei jedem Drücken nur einmal funktionieren. Dies ist jedoch nicht das Verhalten, das im Weltraum benötigt wird, daher wird es auch kommentiert.
Was fehlt uns hier? Was ich hinzugefügt habe:
- Bewegung hin und her entlang der Sichtlinie.
- Die Drehung der Kamera, deren Achse die Sichtlinie ist.
- Autorotation.
- Die Möglichkeit, die Geschwindigkeit aller Bewegungen und Drehungen mit einer Taste zu ändern.
- Volle Tastatursteuerung.
Ich hoffe, der Leser wird mir verzeihen, dass ich über den Bearbeitungsprozess nicht in der Reihenfolge des Abschlusses dieser Unteraufgaben, sondern in der Reihenfolge des Verfolgens des Quellcodes von oben nach unten sprechen werde. Ich werde erklären, was und warum ich tue.
Konstruktor
Fügen Sie einige Variablen hinzu:
this.rotationZFactor = 0.0; this.RVMovingFactor = 0.0; this.autoRotate = false; this.autoRotateSpeed = 0.001; this.allSpeedsFactor = 1;
RotationZFactor wird benötigt, um die Rotation der Kamera auf der Sichtachse zu steuern.
RVMovingFactor zur Steuerung von Vorwärts- und Rückwärtsfahrt entlang derselben Achse,
autoRotate Ich nehme an, es werden keine Kommentare benötigt.
allSpeedsFactor zur Steuerung der Geschwindigkeit aller kombinierten Bewegungen und Kurven.
RotateCamera-Methode
Auf diese Weise organisiert:
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() { ...
Hier war fast alles so, das einzige, was ich hinzugefügt habe, war die Variable tmpQuaternion, um die Drehung der Kamera entlang der Z-Achse zu handhaben.
Außerdem war es ziemlich einfach zu überprüfen, ob eine Verarbeitung erforderlich war:
moveDirection.set( _moveCurr.x - _movePrev.x, _moveCurr.y - _movePrev.y, 0 ); angle = moveDirection.length(); if ( angle ) {
Und es wurde so:
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) {
Was ist hier und warum? Erstens, wenn die automatische Drehung aktiv ist, fügen wir ihre Geschwindigkeit der Drehung entlang der X-Achse hinzu. Zweitens haben wir allSpeedsFactor um die Skalierung aller Drehungen (und auch der automatischen Drehungen) erweitert. Zum Steuern der Geschwindigkeit aller Conversions. Und drittens werden wir weitere Aktionen nicht nur ausführen, wenn der Winkelwinkel (Kamerabewegung relativ zu Steuerelementen) ungleich Null ist, sondern auch, wenn RotationZFactor, der für die Drehung der Kamera entlang der Z-Achse verantwortlich ist, ungleich Null ist.
Weiter im Methodencode, nach Berechnung der Quaternion der Rotation, eine kleine Addition:
quaternion.setFromAxisAngle( axis, angle );
Die Quaternion der Rotation der Kamera um das Objekt multiplizieren wir mit der separat berechneten Quaternion der Rotation entlang der Z-Achse. Jetzt kann sich die Kamera entlang aller drei Achsen drehen.
ZoomCamera-Methode
Ich gebe Ihnen den gesamten Code, er ist klein:
this.zoomCamera = function () { var factor; if ( _state === STATE.TOUCH_ZOOM_PAN ) {
Bei allen Änderungen wird allSpeedsFactor an mehreren Stellen hinzugefügt, um die Geschwindigkeit (Bewegungen, Rotationen) und den Zoom zu steuern.
PanCamera-Methode
Ich werde auch seinen gesamten Code als geben es ist klein:
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 ) ); } } }; }() );
Aber es gibt noch viel mehr Änderungen. Ich werde Ihnen alles in der richtigen Reihenfolge erzählen.
Die Variable rv wird hinzugefügt - ein Vektor zum Verarbeiten der Radialgeschwindigkeit der Kamera, d.h. seine Geschwindigkeit entlang der z-Achse.
Skalierung des mouseChange-Vektors zu demselben allSpeedsFactor hinzugefügt, aber auch hier möchte ich alle Transformationen mithilfe dieser Skalierung auf eine einzige anpassen, sodass sie hinzugefügt wird, um alle Bewegungen zu verarbeiten.
Die Überprüfung des Verarbeitungsbedarfs besteht nun nicht nur, wenn die Maus bewegt wird, sondern auch, wenn RVMovingFactor ungleich Null ist - eine Variable, die für die Bewegung der Kamera entlang der Sichtlinie verantwortlich ist.
Fügen Sie schließlich im Methodencode die Schwenk- und RV-Vektoren hinzu, um die volle Bewegung der Kamera entlang aller drei Achsen zu erhalten, und wenden Sie sie auf die Kamera und auf controls.target an.
Aktualisierungsmethode
Um einige unerwünschte Effekte zu vermeiden, werden am Ende der Methode die folgenden Zeilen hinzugefügt:
_this.RVMovingFactor = 0.0; _this.rotationZFactor = 0.0;
Wie Sie sich vorstellen können, waren unerwünschte Effekte mit Rotation und Bewegung entlang der Z-Achse verbunden.
Keydown-Methode
Der Quellcode, den ich ganz am Anfang der Geschichte gezeigt habe, ist vollständig kommentiert. Und außer ihm gab es praktisch nichts im Körper der Methode, also können wir sagen, dass sie von Grund auf neu geschrieben wurde.
function keydown( event ) { if ( _this.enabled === false ) return; switch (event.keyCode) {
Also, was wurde hier gemacht:
Viele Schaltflächen simulieren Änderungen, die durch Mausbewegungen vorgenommen wurden.
"A", "D" - dupliziert die Kameradrehung nach links und rechts.
"Z", "X" - dupliziert die Kamera, die sich auf und ab dreht.
"+", "-" - Duplizieren Sie den Zoom mit dem Mausrad.
Die Pfeiltasten duplizieren die Bewegung der Kamera nach oben und unten sowie nach links und rechts, ohne den Zoom und den Drehwinkel zu ändern.
Außerdem wurden Funktionen hinzugefügt, bei denen die Tasten der Standardmaus nicht ausreichen würden:
"W", "S" - Bewegung entlang der Sichtlinie ohne Änderung des Zooms.
"Q", "E" - Kameradrehung um die Z-Achse, d.h. um die Sichtlinie.
"R", "Shift" - Verringern Sie die Geschwindigkeit aller Bewegungen, Drehungen, Transformationen um das 3-fache bzw. 20-fache.
In diesem Fall können Sie feststellen, dass die Verwendung des Schlüsselarrays abgelehnt wurde. Für meine Zwecke war dies nicht erforderlich, und um ehrlich zu sein, um das zu vermasseln, was ich persönlich nicht brauche, war ich zu faul.
Keyup-Methode
Ich werde es in seiner Gesamtheit zitieren:
function keyup( event ) { if ( _this.enabled === false ) return; switch (event.keyCode) {
Wie Sie vielleicht erraten haben, müssen lediglich die Werte der Variablen RVMovingFactor, RotationZFactor, allSpeedsFactor auf ihre Standardwerte zurückgesetzt werden.
Außerdem wurde die Installation des Keystroke-Handlers kommentiert, da dessen Entfernung bei der Keydown-Methode entfernt wurde.
Um die geänderte Bibliothek nicht mit der Standardbibliothek zu verwechseln, ändern wir den Namen.
Benennen Sie den Konstruktor um:
THREE.AstroControls = function ( object, domElement ) {
Anstelle von TrackballControls jetzt AstroControls.
Und am Ende der Datei schreiben wir:
THREE.AstroControls.prototype = Object.create( THREE.EventDispatcher.prototype ); THREE.AstroControls.prototype.constructor = THREE.AstroControls;
Alles, neue Bedienelemente sind einsatzbereit.
Sein gesamter Code befindet sich unter dem 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;
Anwendung
Speichern Sie den vollständigen Code in einer Datei, nennen Sie ihn AstroControls.js und fügen Sie ihn Ihrem Projekt hinzu.
Erstellen Sie Steuerelemente wie folgt:
var controls; ...
In Zukunft ist alles Standard, setzen Sie controls.target und controls.autoRotate wie gewohnt. ( ,
three.js , OrbitControls . AstroControls ) , , , touch-events.
, - .