Skip to content

Loading…

Pull Request for Plane3D and ObjectDragControls. #1039

Open
wants to merge 2 commits into from

3 participants

@holmanelias

As specified in #1031, added Plane3D and ObjectDragControls, that can drag an object on an arbitrary, specified Plane3D. Plane3D also contains code to generate a plane from points or a point and a normal, and intersection code. Tested with some basic planes, and included an example of how to use both in the examples folder. Updated build.py to include those two items.

Elias Holman added some commits
Elias Holman Added new Plane3D and ObjectDragControls to allow objects to be dragg…
…ed on an arbitrary plane, and new example, webgl_interactive_simpledragcube.html, which utiltizes that capability
fcc5ba2
Elias Holman Fixed a bug in the mouse coordinate calculation where the x and y wer…
…e not being properly remapped based on the real size of the component. Updated the object class to take a width and height for coordinate remapping, and updated the example to use a 640x480 canvas for rendering to show it working.
dd2bfcf
@zz85 zz85 referenced this pull request
Closed

THREE.DragControls #1535

@bhouston

I recently created a new Plane.js class that is a more bit traditional in design -- it doesn't utilize the check alignment paradigm employed in this Plane3D class. I have submitted the traditional Plane.js class as part of PR #2718 Comments are welcome.

@bhouston

math/Plane.js has been implemented, thus this should be closed as obsolete.

@mrdoob
Owner

@bhouston It would be great to have a ObjectDragControls that uses Plane though. The way we're doing it at the moment it's a bit hacky...

http://mrdoob.github.com/three.js/examples/webgl_interactive_draggablecubes.html

@bhouston

I'll have a look at this.

@mrdoob
Owner

@bhouston any luck?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jan 7, 2012
  1. Added new Plane3D and ObjectDragControls to allow objects to be dragg…

    Elias Holman committed
    …ed on an arbitrary plane, and new example, webgl_interactive_simpledragcube.html, which utiltizes that capability
  2. Fixed a bug in the mouse coordinate calculation where the x and y wer…

    Elias Holman committed
    …e not being properly remapped based on the real size of the component. Updated the object class to take a width and height for coordinate remapping, and updated the example to use a 640x480 canvas for rendering to show it working.
Showing with 311 additions and 0 deletions.
  1. +128 −0 examples/webgl_interactive_simpledragcube.html
  2. +90 −0 src/core/Plane3D.js
  3. +91 −0 src/extras/controls/ObjectDragControls.js
  4. +2 −0 utils/build.py
View
128 examples/webgl_interactive_simpledragcube.html
@@ -0,0 +1,128 @@
+<!doctype html>
+<html lang="en">
+ <head>
+ <title>three.js webgl - draggable cubes</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+ <style>
+ body {
+ font-family: Monospace;
+ background-color: #f0f0f0;
+ margin: 0px;
+ overflow: hidden;
+ }
+ </style>
+ </head>
+ <body>
+
+ <script src="../build/Three.js"></script>
+ <script src="../src/extras/controls/ObjectDragControls.js"></script>
+ <script src="js/Stats.js"></script>
+
+ <script>
+
+ var container, stats;
+ var camera, scene, renderer;
+ init();
+ animate();
+
+ function init() {
+
+ container = document.createElement( 'div' );
+ document.body.appendChild( container );
+
+ //camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 10000 );
+ camera = new THREE.PerspectiveCamera( 70, 640/480, 1, 10000 );
+ camera.position.z = 1000;
+
+ scene = new THREE.Scene();
+
+ scene.add( new THREE.AmbientLight( 0x505050 ) );
+
+ var light = new THREE.SpotLight( 0xffffff, 1.5 );
+ light.position.set( 0, 500, 2000 );
+ light.castShadow = true;
+ scene.add( light );
+
+ var geometry = new THREE.CubeGeometry( 40, 40, 40 );
+ var object = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { color: Math.random() * 0xffffff } ) );
+ object.material.ambient = object.material.color;
+
+ object.position.x = Math.random() * 1000 - 500;
+ object.position.y = Math.random() * 600 - 300;
+ object.position.z = Math.random() * 800 - 400;
+
+ object.rotation.x = ( Math.random() * 360 ) * Math.PI / 180;
+ object.rotation.y = ( Math.random() * 360 ) * Math.PI / 180;
+ object.rotation.z = ( Math.random() * 360 ) * Math.PI / 180;
+
+ object.scale.x = Math.random() * 2 + 1;
+ object.scale.y = Math.random() * 2 + 1;
+ object.scale.z = Math.random() * 2 + 1;
+
+ object.castShadow = true;
+ object.receiveShadow = true;
+
+ scene.add( object );
+
+ renderer = new THREE.WebGLRenderer( { antialias: true } );
+ //Example drag on the Z plane, but any plane (except YZ or XZ since they are
+ //parallel to the default camera position) should work
+ var dragPlane = new Plane3D(0, 0, 1, 0);
+ var dragControl = new ObjectDragControls(dragPlane, camera, container, 640, 480);
+ dragControl.attach(renderer);
+ dragControl.addObject(object)
+ renderer.sortObjects = false;
+ //renderer.setSize( window.innerWidth, window.innerHeight );
+ renderer.setSize(640, 480);
+
+ renderer.shadowMapEnabled = true;
+ renderer.shadowMapSoft = true;
+
+ renderer.shadowCameraNear = 3;
+ renderer.shadowCameraFar = camera.far;
+ renderer.shadowCameraFov = 50;
+
+ renderer.shadowMapBias = 0.0039;
+ renderer.shadowMapDarkness = 0.5;
+ renderer.shadowMapWidth = 1024;
+ renderer.shadowMapHeight = 1024;
+
+ container.appendChild( renderer.domElement );
+
+ var info = document.createElement( 'div' );
+ info.style.position = 'absolute';
+ info.style.top = '10px';
+ info.style.width = '100%';
+ info.style.textAlign = 'center';
+ info.innerHTML = '<a href="http://github.com/mrdoob/three.js" target="_blank">three.js</a> webgl - draggable cubes';
+ container.appendChild( info );
+
+ stats = new Stats();
+ stats.domElement.style.position = 'absolute';
+ stats.domElement.style.top = '0px';
+ container.appendChild( stats.domElement );
+
+ }
+
+ //
+
+ function animate() {
+
+ requestAnimationFrame( animate );
+
+ render();
+ stats.update();
+
+ }
+
+ function render() {
+
+ renderer.render( scene, camera );
+
+ }
+
+ </script>
+
+ </body>
+</html>
View
90 src/core/Plane3D.js
@@ -0,0 +1,90 @@
+
+ /*
+ Adapted from the Away3D Plane3D class by David Lenaerts
+ */
+
+ this.Plane3D = (function() {
+
+ Plane3D.prototype.ALIGN_ANY = 0;
+
+ Plane3D.prototype.ALIGN_XY = 1;
+
+ Plane3D.prototype.ALIGN_YZ = 2;
+
+ Plane3D.prototype.ALIGN_XZ = 3;
+
+ function Plane3D(a, b, c, d) {
+ this.a = a != null ? a : 0;
+ this.b = b != null ? b : 0;
+ this.c = c != null ? c : 0;
+ this.d = d != null ? d : 0;
+ this.checkAlignment();
+ }
+
+ Plane3D.prototype.checkAlignment = function() {
+ this.alignment = Plane3D.prototype.ALIGN_ANY;
+ if (this.a === 0 && this.b === 0) {
+ this.alignment = Plane3D.prototype.ALIGN_XY_AXIS;
+ }
+ if (this.b === 0 && this.c === 0) {
+ this.alignment = Plane3D.prototype.ALIGN_YZ_AXIS;
+ }
+ if (this.a === 0 && this.c === 0) {
+ return this.alignment = Plane3D.prototype.ALIGN_XZ_AXIS;
+ }
+ };
+
+ Plane3D.prototype.fromPoints = function(p0, p1, p2) {
+ var d1x, d1y, d1z, d2x, d2y, d2z;
+ d1x = p1.x - p0.x;
+ d1y = p1.y - p0.y;
+ d1z = p1.z - p0.z;
+ d2x = p2.x - p0.x;
+ d2y = p2.y - p0.y;
+ d2z = p2.z - p0.z;
+ this.a = d1y * d2z - d1z * d2y;
+ this.b = d1z * d2x - d1x * d2z;
+ this.c = d1x * d2y - d1y * d2x;
+ this.d = this.a * p0.x + this.b * p0.y + this.c * p0.z;
+ return this.checkAlignment();
+ };
+
+ Plane3D.prototype.fromPointAndNormal = function(point, normal) {
+ this.a = normal.x;
+ this.b = normal.y;
+ this.c = normal.z;
+ this.d = this.a * point.x + this.b * point.y + this.c * point.z;
+ return this.checkAlignment();
+ };
+
+ Plane3D.prototype.distance = function(point) {
+ switch (this.alignment) {
+ case ALIGN_YZ_AXIS:
+ return this.a * p.x - this.d;
+ case ALIGN_XZ_AXIS:
+ return this.b * p.y - this.d;
+ case ALIGN_XY_AXIS:
+ return this.c * p.z - this.d;
+ default:
+ return this.a * p.x + this.b * p.y + this.c * p.z - this.d;
+ }
+ };
+
+ /*
+ #Returns the point where the given ray and this Plane3D intersect
+ */
+
+ Plane3D.prototype.intersect = function(ray) {
+ var L, Pint, S, V, t;
+ L = new THREE.Vector4(this.a, this.b, this.c, this.d);
+ S = new THREE.Vector4(ray.origin.x, ray.origin.y, ray.origin.z, 1);
+ V = new THREE.Vector4(ray.direction.x, ray.direction.y, ray.direction.z, 0);
+ if (L.dot(V) === 0) return NaN;
+ t = -(L.dot(S) / L.dot(V));
+ Pint = S.addSelf(V.multiplyScalar(t));
+ return new THREE.Vector3(Pint.x, Pint.y, Pint.z);
+ };
+
+ return Plane3D;
+
+ })();
View
91 src/extras/controls/ObjectDragControls.js
@@ -0,0 +1,91 @@
+(function() {
+ var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
+
+ this.ObjectDragControls = (function() {
+
+ function ObjectDragControls(dragPlane, camera, container, width, height) {
+ this.dragPlane = dragPlane;
+ this.camera = camera;
+ this.container = container;
+ this.width = width;
+ this.height = height;
+ this.onDocumentMouseUp = __bind(this.onDocumentMouseUp, this);
+ this.onDocumentMouseDown = __bind(this.onDocumentMouseDown, this);
+ this.onDocumentMouseMove = __bind(this.onDocumentMouseMove, this);
+ this.objects = [];
+ this.mouse = new THREE.Vector2();
+ this.projector = new THREE.Projector();
+ this.selected = false;
+ this.intersected = false;
+ if (typeof jQuery !== 'undefined' && this.container instanceof jQuery) {
+ this.container = this.container[0];
+ }
+ }
+
+ ObjectDragControls.prototype.addObject = function(object) {
+ return this.objects.push(object);
+ };
+
+ ObjectDragControls.prototype.attach = function(renderer) {
+ this.renderer = renderer;
+ this.renderer.domElement.addEventListener('mousemove', this.onDocumentMouseMove, false);
+ this.renderer.domElement.addEventListener('mousedown', this.onDocumentMouseDown, false);
+ return this.renderer.domElement.addEventListener('mouseup', this.onDocumentMouseUp, false);
+ };
+
+ ObjectDragControls.prototype.onDocumentMouseMove = function(event) {
+ var intersectionPt, intersects, ray, vector;
+ event.preventDefault();
+ this.mouse.x = (event.clientX / this.width) * 2 - 1;
+ this.mouse.y = -(event.clientY / this.height) * 2 + 1;
+ vector = new THREE.Vector3(this.mouse.x, this.mouse.y, 0.5);
+ this.projector.unprojectVector(vector, this.camera);
+ ray = new THREE.Ray(this.camera.position, vector.subSelf(this.camera.position).normalize());
+ if (this.selected) {
+ intersectionPt = this.dragPlane.intersect(ray);
+ this.selected.position.copy(intersectionPt);
+ return;
+ }
+ intersects = ray.intersectObjects(this.objects);
+ if (intersects.length > 0) {
+ if (this.intersected !== intersects[0].object) {
+ if (this.intersected) {
+ this.intersected.material.color.setHex(this.intersected.currentHex);
+ }
+ this.intersected = intersects[0].object;
+ this.intersected.currentHex = this.intersected.material.color.getHex();
+ }
+ return this.container.style.cursor = 'pointer';
+ } else {
+ if (this.intersected) {
+ this.intersected.material.color.setHex(this.intersected.currentHex);
+ }
+ this.intersected = null;
+ return this.container.style.cursor = 'auto';
+ }
+ };
+
+ ObjectDragControls.prototype.onDocumentMouseDown = function(event) {
+ var intersects, ray, vector;
+ event.preventDefault();
+ vector = new THREE.Vector3(this.mouse.x, this.mouse.y, 0.5);
+ this.projector.unprojectVector(vector, this.camera);
+ ray = new THREE.Ray(this.camera.position, vector.subSelf(this.camera.position).normalize());
+ intersects = ray.intersectObjects(this.objects);
+ if (intersects.length > 0) {
+ this.selected = intersects[0].object;
+ return this.container.style.cursor = 'move';
+ }
+ };
+
+ ObjectDragControls.prototype.onDocumentMouseUp = function(event) {
+ event.preventDefault();
+ this.selected = null;
+ return this.container.style.cursor = 'auto';
+ };
+
+ return ObjectDragControls;
+
+ })();
+
+}).call(this);
View
2 utils/build.py
@@ -25,6 +25,7 @@
'core/Matrix3.js',
'core/Matrix4.js',
'core/Object3D.js',
+'core/Plane3D.js',
'core/Projector.js',
'core/Quaternion.js',
'core/Vertex.js',
@@ -111,6 +112,7 @@
'extras/controls/PathControls.js',
'extras/controls/FlyControls.js',
'extras/controls/RollControls.js',
+'extras/controls/ObjectDragControls.js',
'extras/controls/TrackballControls.js',
'extras/geometries/CubeGeometry.js',
'extras/geometries/CylinderGeometry.js',
Something went wrong with that request. Please try again.