Add a basic drag with aabb support

dev
Unboring SL 5 years ago
parent 103e404dd9
commit c96d57ccb2
  1. 85773
      dist/aframe-master.js
  2. 2
      dist/index.html
  3. 2
      package.json
  4. 105
      src/components/aabb-collider.js
  5. 19
      src/components/drag-controls.js
  6. 14
      src/home.html
  7. 8
      src/index.js
  8. 282
      src/lib/DragControls.js

85773
dist/aframe-master.js vendored

File diff suppressed because one or more lines are too long

2
dist/index.html vendored

@ -22,7 +22,7 @@
} }
</style> </style>
<link rel="shortcut icon" href="assets/img/favicon.png" type="image/x-icon"> <link rel="shortcut icon" href="assets/img/favicon.png" type="image/x-icon">
<script src="https://aframe.io/releases/1.0.3/aframe.min.js"></script> <script src="aframe-master.js"></script>
<script src="bundle.js"></script> <script src="bundle.js"></script>
</head> </head>
<body> <body>

@ -16,8 +16,8 @@
"keywords": [], "keywords": [],
"author": "Arturo Paracuellos", "author": "Arturo Paracuellos",
"devDependencies": { "devDependencies": {
"aframe-aabb-collider-component": "^3.2.0",
"aframe-environment-component": "^2.0.0", "aframe-environment-component": "^2.0.0",
"aframe-event-set-component": "^5.0.0",
"aframe-super-hot-loader": "^1.7.0", "aframe-super-hot-loader": "^1.7.0",
"aframe-super-hot-html-loader": "^2.1.0", "aframe-super-hot-html-loader": "^2.1.0",
"babel-core": "^6.26.3", "babel-core": "^6.26.3",

@ -0,0 +1,105 @@
/* global AFRAME, THREE */
/**
* Implement AABB collision detection for entities with a mesh.
* (https://en.wikipedia.org/wiki/Minimum_bounding_box#Axis-aligned_minimum_bounding_box)
* It sets the specified state on the intersected entities.
*
* @property {string} objects - Selector of the entities to test for collision.
* @property {string} state - State to set on collided entities.
*
*/
AFRAME.registerComponent('aabb-collider', {
schema: {
objects: {default: ''},
state: {default: 'collided'}
},
init: function () {
this.els = [];
this.collisions = [];
this.elMax = new THREE.Vector3();
this.elMin = new THREE.Vector3();
},
/**
* Update list of entities to test for collision.
*/
update: function () {
var data = this.data;
var objectEls;
// Push entities into list of els to intersect.
if (data.objects) {
objectEls = this.el.sceneEl.querySelectorAll(data.objects);
} else {
// If objects not defined, intersect with everything.
objectEls = this.el.sceneEl.children;
}
// Convert from NodeList to Array
this.els = Array.prototype.slice.call(objectEls);
},
tick: (function () {
var boundingBox = new THREE.Box3();
return function () {
var collisions = [];
var el = this.el;
var mesh = el.getObject3D('mesh');
var self = this;
// No mesh, no collisions
if (!mesh) { return; }
// Update the bounding box to account for rotations and
// position changes.
updateBoundingBox();
// Update collisions.
this.els.forEach(intersect);
// Emit events.
collisions.forEach(handleHit);
// No collisions.
if (collisions.length === 0) { self.el.emit('hit', {el: null}); }
// Updated the state of the elements that are not intersected anymore.
this.collisions.filter(function (el) {
return collisions.indexOf(el) === -1;
}).forEach(function removeState (el) {
el.removeState(self.data.state);
el.emit('hitend');
});
// Store new collisions
this.collisions = collisions;
// AABB collision detection
function intersect (el) {
var intersected;
var mesh = el.getObject3D('mesh');
var elMin;
var elMax;
if (!mesh) { return; }
boundingBox.setFromObject(mesh);
elMin = boundingBox.min;
elMax = boundingBox.max;
// Bounding boxes are always aligned with the world coordinate system.
// The collision test checks for the conditions where cubes intersect.
// It's an extension to 3 dimensions of this approach (with the condition negated)
// https://www.youtube.com/watch?v=ghqD3e37R7E
intersected = (self.elMin.x <= elMax.x && self.elMax.x >= elMin.x) &&
(self.elMin.y <= elMax.y && self.elMax.y >= elMin.y) &&
(self.elMin.z <= elMax.z && self.elMax.z >= elMin.z);
if (!intersected) { return; }
collisions.push(el);
}
function handleHit (hitEl) {
hitEl.emit('hit');
hitEl.addState(self.data.state);
self.el.emit('hit', {el: hitEl});
}
function updateBoundingBox () {
boundingBox.setFromObject(mesh);
self.elMin.copy(boundingBox.min);
self.elMax.copy(boundingBox.max);
}
};
})()
});

@ -1,19 +0,0 @@
AFRAME.registerComponent('drag-controls', {
schema: {objects: {type: 'string', default: 'a-entity'}},
init: function () {
var objects = [];
var el = this.el;
var els;
var dragControls;
els = el.sceneEl.querySelectorAll(this.data.objects);
for (var i = 0; i < els.length; i++) {
console.log(i);
objects.push(els[i].object3D);
}
el.sceneEl.addEventListener('renderstart', function () {
dragControls = new THREE.DragControls(objects, el.sceneEl.camera, el.sceneEl.renderer.domElement);
});
}
});

@ -1,4 +1,4 @@
<a-scene drag-controls="objects: .cube" background="color: #FAFAFA" vr-mode-ui="enterVRButton: #vrButton"> <a-scene background="color: #FAFAFA" vr-mode-ui="enterVRButton: #vrButton">
<a-assets> <a-assets>
<a-mixin id="cube" <a-mixin id="cube"
event-set__grab="material.color: #FFEF4F" event-set__grab="material.color: #FFEF4F"
@ -9,18 +9,18 @@
event-set__mouseenter="material.color: #F2E646" event-set__mouseenter="material.color: #F2E646"
event-set__mouseleave="material.color: #EF2D5E" event-set__mouseleave="material.color: #EF2D5E"
event-set__mouseup="material.color: #F2E646" event-set__mouseup="material.color: #F2E646"
geometry="primitive: box; height: 0.30; width: 0.30; depth: 0.30" geometry="primitive: box; height: 0.30; width: 0.30; depth: 0.05"
material="color: #EF2D5E;"> material="color: #EF2D5E;">
</a-mixin> </a-mixin>
</a-assets> </a-assets>
<a-entity class="cube" mixin="cube" position="0.30 1.65 0"></a-entity> <a-entity position="0 0 -0.5">
<a-entity class="cube" mixin="cube" position="0 1.95 0"></a-entity> <a-entity class="cube" mixin="cube" position="0.30 1.05 0"></a-entity>
<a-entity class="cube" mixin="cube" position="-0.30 1.65 0"></a-entity> <a-entity class="cube" mixin="cube" position="0 1.35 0"></a-entity>
<a-entity class="cube" mixin="cube" position="-0.30 1.05 0"></a-entity>
</a-entity>
<a-entity environment="preset: forest;"></a-entity> <a-entity environment="preset: forest;"></a-entity>
<a-entity hand-controls="left" aabb-collider="objects: .cube;" grab></a-entity> <a-entity hand-controls="left" aabb-collider="objects: .cube;" grab></a-entity>
<a-entity hand-controls="right" aabb-collider="objects: .cube;" grab></a-entity> <a-entity hand-controls="right" aabb-collider="objects: .cube;" grab></a-entity>
<a-entity position="0 1.6 2" camera look-controls wasd-controls></a-entity>
</a-scene> </a-scene>

@ -3,11 +3,11 @@ function requireAll (req) { req.keys().forEach(req); }
console.time = () => {}; console.time = () => {};
console.timeEnd = () => {}; console.timeEnd = () => {};
require('aframe-environment-component') require('aframe-environment-component');
require('aframe-aabb-collider-component'); require('aframe-event-set-component');
require('./lib/DragControls.js');
require('./components/aabb-collider');
require('./components/grab'); require('./components/grab');
require('./components/drag-controls');
require('./index.css') require('./index.css')

@ -1,282 +0,0 @@
/*
* @author zz85 / https://github.com/zz85
* @author mrdoob / http://mrdoob.com
* Running this will allow you to drag three.js objects around the screen.
*/
THREE.DragControls = function ( _objects, _camera, _domElement ) {
if ( _objects instanceof THREE.Camera ) {
console.warn( 'THREE.DragControls: Constructor now expects ( objects, camera, domElement )' );
var temp = _objects; _objects = _camera; _camera = temp;
}
var _plane = new THREE.Plane();
var _raycaster = new THREE.Raycaster();
var _mouse = new THREE.Vector2();
var _offset = new THREE.Vector3();
var _intersection = new THREE.Vector3();
var _selected = null, _hovered = null;
//
var scope = this;
function activate() {
_domElement.addEventListener( 'mousemove', onDocumentMouseMove, false );
_domElement.addEventListener( 'mousedown', onDocumentMouseDown, false );
_domElement.addEventListener( 'mouseup', onDocumentMouseCancel, false );
_domElement.addEventListener( 'mouseleave', onDocumentMouseCancel, false );
_domElement.addEventListener( 'touchmove', onDocumentTouchMove, false );
_domElement.addEventListener( 'touchstart', onDocumentTouchStart, false );
_domElement.addEventListener( 'touchend', onDocumentTouchEnd, false );
}
function deactivate() {
_domElement.removeEventListener( 'mousemove', onDocumentMouseMove, false );
_domElement.removeEventListener( 'mousedown', onDocumentMouseDown, false );
_domElement.removeEventListener( 'mouseup', onDocumentMouseCancel, false );
_domElement.removeEventListener( 'mouseleave', onDocumentMouseCancel, false );
_domElement.removeEventListener( 'touchmove', onDocumentTouchMove, false );
_domElement.removeEventListener( 'touchstart', onDocumentTouchStart, false );
_domElement.removeEventListener( 'touchend', onDocumentTouchEnd, false );
}
function dispose() {
deactivate();
}
function onDocumentMouseMove( event ) {
event.preventDefault();
var rect = _domElement.getBoundingClientRect();
_mouse.x = ( ( event.clientX - rect.left ) / rect.width ) * 2 - 1;
_mouse.y = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1;
_raycaster.setFromCamera( _mouse, _camera );
if ( _selected && scope.enabled ) {
if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
_selected.position.copy( _intersection.sub( _offset ) );
}
scope.dispatchEvent( { type: 'drag', object: _selected } );
return;
}
_raycaster.setFromCamera( _mouse, _camera );
var intersects = _raycaster.intersectObjects( _objects, true );
if ( intersects.length > 0 ) {
var object = intersects[ 0 ].object.parent;
_plane.setFromNormalAndCoplanarPoint( _camera.getWorldDirection( _plane.normal ), object.position );
if ( _hovered !== object ) {
scope.dispatchEvent( { type: 'hoveron', object: object } );
_domElement.style.cursor = 'pointer';
_hovered = object;
}
} else {
if ( _hovered !== null ) {
scope.dispatchEvent( { type: 'hoveroff', object: _hovered } );
_domElement.style.cursor = 'auto';
_hovered = null;
}
}
}
function onDocumentMouseDown( event ) {
event.preventDefault();
_raycaster.setFromCamera( _mouse, _camera );
var intersects = _raycaster.intersectObjects( _objects, true );
if ( intersects.length > 0 ) {
_selected = intersects[ 0 ].object.parent;
if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
_offset.copy( _intersection ).sub( _selected.position );
}
_domElement.style.cursor = 'move';
scope.dispatchEvent( { type: 'dragstart', object: _selected } );
}
}
function onDocumentMouseCancel( event ) {
event.preventDefault();
if ( _selected ) {
scope.dispatchEvent( { type: 'dragend', object: _selected } );
_selected = null;
}
_domElement.style.cursor = 'auto';
}
function onDocumentTouchMove( event ) {
event.preventDefault();
event = event.changedTouches[ 0 ];
var rect = _domElement.getBoundingClientRect();
_mouse.x = ( ( event.clientX - rect.left ) / rect.width ) * 2 - 1;
_mouse.y = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1;
_raycaster.setFromCamera( _mouse, _camera );
if ( _selected && scope.enabled ) {
if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
_selected.position.copy( _intersection.sub( _offset ) );
}
scope.dispatchEvent( { type: 'drag', object: _selected } );
return;
}
}
function onDocumentTouchStart( event ) {
event.preventDefault();
event = event.changedTouches[ 0 ];
var rect = _domElement.getBoundingClientRect();
_mouse.x = ( ( event.clientX - rect.left ) / rect.width ) * 2 - 1;
_mouse.y = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1;
_raycaster.setFromCamera( _mouse, _camera );
var intersects = _raycaster.intersectObjects( _objects, true );
if ( intersects.length > 0 ) {
_selected = intersects[ 0 ].object.parent;
_plane.setFromNormalAndCoplanarPoint( _camera.getWorldDirection( _plane.normal ), _selected.position );
if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
_offset.copy( _intersection ).sub( _selected.position );
}
_domElement.style.cursor = 'move';
scope.dispatchEvent( { type: 'dragstart', object: _selected } );
}
}
function onDocumentTouchEnd( event ) {
event.preventDefault();
if ( _selected ) {
scope.dispatchEvent( { type: 'dragend', object: _selected } );
_selected = null;
}
_domElement.style.cursor = 'auto';
}
activate();
// API
this.enabled = true;
this.activate = activate;
this.deactivate = deactivate;
this.dispose = dispose;
// Backward compatibility
this.setObjects = function () {
console.error( 'THREE.DragControls: setObjects() has been removed.' );
};
this.on = function ( type, listener ) {
console.warn( 'THREE.DragControls: on() has been deprecated. Use addEventListener() instead.' );
scope.addEventListener( type, listener );
};
this.off = function ( type, listener ) {
console.warn( 'THREE.DragControls: off() has been deprecated. Use removeEventListener() instead.' );
scope.removeEventListener( type, listener );
};
this.notify = function ( type ) {
console.error( 'THREE.DragControls: notify() has been deprecated. Use dispatchEvent() instead.' );
scope.dispatchEvent( { type: type } );
};
};
THREE.DragControls.prototype = Object.create( THREE.EventDispatcher.prototype );
THREE.DragControls.prototype.constructor = THREE.DragControls;
Loading…
Cancel
Save