<!DOCTYPE html>
<html>
<head>
<title>Bézierova ploha s interaktivnim kontrolnim točkama</title>
<meta charset="UTF-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js" integrity="sha512-bLT0Qm9VnAYZDflyKcBaQ2gg0hSYNQrJ8RilYldYQ1FxQYoCLtUjuuRuZo+fjqhx/qtq/1itJ0C2ejDxltZVFg==" crossorigin="anonymous"></script>
<style>
body {
margin: 0;
overflow: hidden;
}
</style>
</head>
<body>
<div id="WebGL-output"></div>
<script type="text/javascript">
$(document).ready(function() {
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
var cameraControls = new THREE.OrbitControls(camera);
var renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setClearColor(0xEEEEEE);
renderer.setSize(window.innerWidth, window.innerHeight);
var axisHelper = new THREE.AxisHelper(20);
$(window).on("resize", function() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
renderScene();
});
camera.position.x = -60;
camera.position.y = 50;
camera.position.z = -40;
camera.lookAt(new THREE.Vector3(20,0,15));
camera.updateProjectionMatrix();
$(cameraControls).on("change", renderScene);
cameraControls.target = new THREE.Vector3(20,0,15);
// interactive controls
var controls = new function() {
this.color = 0x17a6ff;
this.x = 0.0;
this.y = 0.0;
this.z = 0.0;
this.showControlPoints = false;
this.wireframe = false;
this.bezierCurves = 51;
this.interactiveCamera = true;
this.axisHelper = false;
}
var gui = new dat.GUI({width:370});
gui.addColor(controls, "color").name("Boja").onChange(function(c) {
bezierSurfaceMaterial.color.setHex(c);
setControlPointsColor(c);
renderScene();
});
gui.add(controls, "wireframe").name("Žičani model").onChange(function(v) {
bezierSurfaceMaterial.wireframe = v;
renderScene();
});
gui.add(controls, "bezierCurves").min(10).max(100).step(1).name("Broj Bézierovih krivulja").onFinishChange(function(n) {
bezierCurveDivisions = n-1; // interpolation will give additional 1 bezier curve
redrawBezierSurface();
renderScene();
});
gui.add(controls, "interactiveCamera").name("Interaktivna kamera").onChange(function(v) {
cameraControls.enabled = v;
});
gui.add(controls, "axisHelper").name("Pomoćne koordinatne osi").onChange(function(v) {
if(v) scene.add(axisHelper);
else scene.remove(axisHelper);
renderScene();
});
var controlx, controly, controlz;
gui.add(controls, "showControlPoints").name("Prikaži kontrolne točke").onChange(function(visibility) {
if (visibility) {
controlx = gui.add(controls, "x").listen();
controly = gui.add(controls, "y").listen();
controlz = gui.add(controls, "z").listen();
} else {
gui.remove(controlx);
gui.remove(controly);
gui.remove(controlz);
}
updateActivePointControls();
setControlPointsVisibility(visibility);
renderScene();
});
function updateActivePointControls() {
controls.x = activeControlPoint.position.x;
controls.y = activeControlPoint.position.y;
controls.z = activeControlPoint.position.z;
controls.x.toFixed(2);
}
// working with bezier curves and surface
var bezierCurveDivisions = 1;
var bezierSurface, bezierSurfaceGeometry, bezierSurfaceMaterial;
var bezierControlPoints = [
[new THREE.Vector3(-10, 10, 0),
new THREE.Vector3(0, 7, 0),
new THREE.Vector3(15, 3, 0),
new THREE.Vector3(30, 8, 0)],
[new THREE.Vector3(-10, 0, 10),
new THREE.Vector3(-5, 15, 10),
new THREE.Vector3(20, 10, 10),
new THREE.Vector3(30, 5, 10)],
[new THREE.Vector3(-10, 5, 20),
new THREE.Vector3(-5,-10, 20),
new THREE.Vector3(10, 10, 20),
new THREE.Vector3(30, 0, 20)],
[new THREE.Vector3(-10, 4, 30),
new THREE.Vector3(-5, 8, 30),
new THREE.Vector3(20, 6, 30),
new THREE.Vector3(30, 4, 30)]
]
function redrawBezierSurface() {
scene.remove(bezierSurface);
var basicBezierModel = []; // 4 bezier curves calculated from bezier control points
// calculating basic bezier model (main 4 bezier curves)
for (var i=0; i < bezierControlPoints.length; i++) {
var bezier = new THREE.CubicBezierCurve3(
bezierControlPoints[i][0],
bezierControlPoints[i][1],
bezierControlPoints[i][2],
bezierControlPoints[i][3]
)
basicBezierModel.push( bezier.getPoints(bezierCurveDivisions) );
}
var bezierCurvesVertices = [];
// calculating full bezier model (50 bezier curves in one direction, each containing 50 vertices)
for (var i=0; i <= bezierCurveDivisions; i++) {
var bezier = new THREE.CubicBezierCurve3(
basicBezierModel[0][i],
basicBezierModel[1][i],
basicBezierModel[2][i],
basicBezierModel[3][i]
)
bezierCurvesVertices = bezierCurvesVertices.concat( bezier.getPoints(bezierCurveDivisions) );
}
// now we've got full bezier model, it's time to create bezier surface and add it to the scene
var bezierSurfaceVertices = bezierCurvesVertices;
var bezierSurfaceFaces = [];
const bezierUVs = [];
const imgLoader = new THREE.ImageLoader();
imgLoader.load(
"https://threejsfundamentals.org/threejs/resources/images/heightmap-96x64.png",
createHeightmap
);
function createHeightmap(image) {
// extract the data from the image by drawing it to a canvas
// and calling getImageData
const ctx = document.createElement("canvas").getContext("2d");
const { width, height } = image;
ctx.canvas.width = width;
ctx.canvas.height = height;
ctx.drawImage(image, 0, 0);
const { data } = ctx.getImageData(0, 0, width, height);
console.log(width);
const cellsAcross =50 - 1;
const cellsDeep = 50 - 1;
for (let z = 0; z < cellsDeep; ++z) {
for (let x = 0; x < cellsAcross; ++x) {
const u0 = x / cellsAcross;
const v0 = z / cellsDeep;
bezierUVs.push([
new THREE.Vector2(0, 0),
new THREE.Vector2(0, 1 ),
new THREE.Vector2(1 ,1 ),
]); bezierUVs.push([
new THREE.Vector2(1 ,1 ),
new THREE.Vector2(0, 0),
new THREE.Vector2(1 , 0),
]); new THREE.Vector2(1,1),
}
}
}
// creating faces from vertices
var v1, v2, v2; // vertex indices in bezierSurfaceVertices array
for (var i=0; i < bezierCurveDivisions; i++) {
for (var j=0; j < bezierCurveDivisions; j++) {
v1 = i * (bezierCurveDivisions + 1) + j;
v2 = (i+1) * (bezierCurveDivisions + 1) + j;
v3 = i * (bezierCurveDivisions + 1) + (j+1);
bezierSurfaceFaces.push( new THREE.Face3(v1, v2, v3) );
v1 = (i+1) * (bezierCurveDivisions + 1) + j;
v2 = (i+1) * (bezierCurveDivisions + 1) + (j+1);
v3 = i * (bezierCurveDivisions + 1) + (j+1);
bezierSurfaceFaces.push( new THREE.Face3(v1, v2, v3) );
}
}
bezierSurfaceGeometry = new THREE.Geometry();
bezierSurfaceGeometry.vertices = bezierSurfaceVertices;
bezierSurfaceGeometry.faces = bezierSurfaceFaces;
bezierSurfaceGeometry.faceVertexUvs[0] = bezierUVs;
bezierSurfaceGeometry.computeFaceNormals();
bezierSurfaceGeometry.computeVertexNormals();
function createMaterial() {
// 4096 is the maximum width for maps
var loader = new THREE.TextureLoader();
loader.setCrossOrigin('anonymous')
var earthTexture=loader.load("https://images-na.ssl-images-amazon.com/images/I/41Xe%2B6VIgtL.jpg");
var earthMaterial = new THREE.MeshBasicMaterial();
earthMaterial.map = earthTexture;
return earthMaterial;
}
bezierSurfaceMaterial = createMaterial();
//bezierSurfaceMaterial = new THREE.MeshLambertMaterial({color: controls.color, wireframe: controls.wireframe});
bezierSurface = new THREE.Mesh(bezierSurfaceGeometry, bezierSurfaceMaterial);
bezierSurface.material.side = THREE.DoubleSide;
scene.add(bezierSurface);
}
redrawBezierSurface();
// drawing control points (spheres)
var activeControlPoint;
var controlPoints = [];
(function() {
for (var i=0; i < 4; i++) { // 4 control points in one direction
for (var j=0; j < 4; j++) { // 4 control points in another direction
if (i == 0 && j == 0) { // first control point becomes active control point
var controlPointGeometry = new THREE.SphereGeometry(0.7,10,10);
} else {
var controlPointGeometry = new THREE.SphereGeometry(0.7,10,10);
}
var controlPointMaterial = new THREE.MeshLambertMaterial({color: 0xffffff - controls.color});
var controlPoint = new THREE.Mesh(controlPointGeometry, controlPointMaterial);
controlPoint.name = + i.toString() + "-" + j.toString();
controlPoint.position.x = bezierControlPoints[i][j].x;
controlPoint.position.y = bezierControlPoints[i][j].y;
controlPoint.position.z = bezierControlPoints[i][j].z;
controlPoint.visible = false;
if (i==0 && j==0) {
controlPoint.scale.set(1.5, 1.5, 1.5);
activeControlPoint = controlPoint;
}
controlPoints.push(controlPoint);
scene.add(controlPoint);
}
}
}) ();
// set control points visible or invisible
function setControlPointsVisibility(visibility) {
$.each(controlPoints, function(k, v) {
v.visible = visibility;
});
}
// set color of control points
function setControlPointsColor(color) {
$.each(controlPoints, function(k, v) {
v.material.color.setHex(0xffffff - controls.color);
});
}
function updateActiveControlPointPosition(x, y, z) {
activeControlPoint.position.set(x, y, z);
var i = activeControlPoint.name.split("-"); // active control point index
bezierControlPoints[ i[0] ][ i[1] ] = new THREE.Vector3(x, y, z);
updateActivePointControls();
redrawBezierSurface();
renderScene();
}
document.addEventListener("mousedown", onControlPointClick, true); // done with pure javascript (without jquery because jquery doesn't support event capturing, which is essential if we want to avoid bugs with mousedown event, which is used for both rotation and placement of control points)
function onControlPointClick(e) {
var mouse = new THREE.Vector2();
var raycaster = new THREE.Raycaster();
mouse.x = (e.clientX / renderer.domElement.width) * 2 - 1;
mouse.y = - (e.clientY / renderer.domElement.height) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
var intersectedObjects = raycaster.intersectObjects(controlPoints);
if (intersectedObjects.length > 0) {
cameraControls.enabled = false; // disabling camera rotation
var newActiveControlPoint = intersectedObjects[0].object;
if (newActiveControlPoint.visible) {
var oldActiveControlPoint = activeControlPoint;
oldActiveControlPoint.scale.set(1, 1, 1);
newActiveControlPoint.scale.set(1.5, 1.5, 1.5);
activeControlPoint = newActiveControlPoint;
updateActivePointControls();
renderScene();
var planeNormal = activeControlPoint.position.clone().sub(camera.position);
var plane = new THREE.Plane();
plane.setFromNormalAndCoplanarPoint(planeNormal, activeControlPoint.position);
$(document).on("mousemove", function(e) {
var mouseMove = new THREE.Vector3();
mouseMove.x = (e.clientX / renderer.domElement.width) * 2 - 1;
mouseMove.y = - (e.clientY / renderer.domElement.height) * 2 + 1;
mouseMove.z = 1;
mouseMove.unproject(camera);
var ray = new THREE.Ray(camera.position, mouseMove.sub(camera.position).normalize());
var intersection = ray.intersectPlane(plane);
updateActiveControlPointPosition(intersection.x, intersection.y, intersection.z);
});
$(document).on("mouseup", function(e) {
$(document).off("mousemove");
$(document).off("mouseup");
cameraControls.enabled = true; // enabling camera controls again
});
}
}
}
// all kinds of lights
var ambientLight = new THREE.AmbientLight(0x0c0c0c);
scene.add(ambientLight);
var spotLightBelow = new THREE.SpotLight(0xffffff);
spotLightBelow.position.set(20, -40, 20);
spotLightBelow.target = bezierSurface;
spotLightBelow.exponent = 5;
scene.add(spotLightBelow);
var spotLightAbove = new THREE.SpotLight(0xffffff);
spotLightAbove.position.set(20,40,20);
spotLightAbove.target = bezierSurface;
spotLightAbove.exponent = 3;
scene.add(spotLightAbove);
// functions for rendering and updating scene
function renderScene() {
renderer.render(scene, camera);
}
function update() {
cameraControls.update();
requestAnimationFrame(update);
}
update();
$("#WebGL-output").append(renderer.domElement);
renderScene();
});
</script>
</body>
</html>
/**
* @author qiao / https://github.com/qiao
* @author mrdoob / http://mrdoob.com
* @author alteredq / http://alteredqualia.com/
* @author WestLangley / http://github.com/WestLangley
* @author erich666 / http://erichaines.com
* @author mrflix / http://felixniklas.de
*/
/*global THREE, console */
// This set of controls performs orbiting, dollying (zooming), and panning. It maintains
// the "up" direction as +Y, unlike the TrackballControls. Touch on tablet and phones is
// supported.
//
// Orbit - left mouse / touch: one finger move
// Zoom - middle mouse, or mousewheel / touch: two finger spread or squish
// Pan - right mouse, or arrow keys / touch: three finter swipe
//
// This is a drop-in replacement for (most) TrackballControls used in examples.
// That is, include this js file and wherever you see:
// controls = new THREE.TrackballControls( camera );
// controls.target.z = 150;
// Simple substitute "OrbitControls" and the control should work as-is.
THREE.OrbitControls = function ( object, domElement, localElement ) {
this.object = object;
this.domElement = ( domElement !== undefined ) ? domElement : document;
this.localElement = ( localElement !== undefined ) ? localElement : document;
// API
// Set to false to disable this control
this.enabled = true;
// "target" sets the location of focus, where the control orbits around
// and where it pans with respect to.
this.target = new THREE.Vector3();
// center is old, deprecated; use "target" instead
this.center = this.target;
// This option actually enables dollying in and out; left as "zoom" for
// backwards compatibility
this.noZoom = false;
this.zoomSpeed = 1.0;
// Limits to how far you can dolly in and out
this.minDistance = 0;
this.maxDistance = Infinity;
// Set to true to disable this control
this.noRotate = false;
this.rotateSpeed = 1.0;
// Set to true to disable this control
this.noPan = false;
this.keyPanSpeed = 7.0; // pixels moved per arrow key push
// Set to true to automatically rotate around the target
this.autoRotate = false;
this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
// How far you can orbit vertically, upper and lower limits.
// Range is 0 to Math.PI radians.
this.minPolarAngle = 0; // radians
this.maxPolarAngle = Math.PI; // radians
// Set to true to disable use of the keys
this.noKeys = false;
// The four arrow keys
this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
////////////
// internals
var scope = this;
var EPS = 0.000001;
var rotateStart = new THREE.Vector2();
var rotateEnd = new THREE.Vector2();
var rotateDelta = new THREE.Vector2();
var panStart = new THREE.Vector2();
var panEnd = new THREE.Vector2();
var panDelta = new THREE.Vector2();
var dollyStart = new THREE.Vector2();
var dollyEnd = new THREE.Vector2();
var dollyDelta = new THREE.Vector2();
var phiDelta = 0;
var thetaDelta = 0;
var scale = 1;
var pan = new THREE.Vector3();
var lastPosition = new THREE.Vector3();
var STATE = { NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 };
var state = STATE.NONE;
// events
var changeEvent = { type: 'change' };
this.rotateLeft = function ( angle ) {
if ( angle === undefined ) {
angle = getAutoRotationAngle();
}
thetaDelta -= angle;
};
this.rotateUp = function ( angle ) {
if ( angle === undefined ) {
angle = getAutoRotationAngle();
}
phiDelta -= angle;
};
// pass in distance in world space to move left
this.panLeft = function ( distance ) {
var panOffset = new THREE.Vector3();
var te = this.object.matrix.elements;
// get X column of matrix
panOffset.set( te[0], te[1], te[2] );
panOffset.multiplyScalar(-distance);
pan.add( panOffset );
};
// pass in distance in world space to move up
this.panUp = function ( distance ) {
var panOffset = new THREE.Vector3();
var te = this.object.matrix.elements;
// get Y column of matrix
panOffset.set( te[4], te[5], te[6] );
panOffset.multiplyScalar(distance);
pan.add( panOffset );
};
// main entry point; pass in Vector2 of change desired in pixel space,
// right and down are positive
this.pan = function ( delta ) {
var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
if ( scope.object.fov !== undefined ) {
// perspective
var position = scope.object.position;
var offset = position.clone().sub( scope.target );
var targetDistance = offset.length();
// half of the fov is center to top of screen
targetDistance *= Math.tan( (scope.object.fov/2) * Math.PI / 180.0 );
// we actually don't use screenWidth, since perspective camera is fixed to screen height
scope.panLeft( 2 * delta.x * targetDistance / element.clientHeight );
scope.panUp( 2 * delta.y * targetDistance / element.clientHeight );
} else if ( scope.object.top !== undefined ) {
// orthographic
scope.panLeft( delta.x * (scope.object.right - scope.object.left) / element.clientWidth );
scope.panUp( delta.y * (scope.object.top - scope.object.bottom) / element.clientHeight );
} else {
// camera neither orthographic or perspective - warn user
console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
}
};
this.dollyIn = function ( dollyScale ) {
if ( dollyScale === undefined ) {
dollyScale = getZoomScale();
}
scale /= dollyScale;
};
this.dollyOut = function ( dollyScale ) {
if ( dollyScale === undefined ) {
dollyScale = getZoomScale();
}
scale *= dollyScale;
};
this.update = function () {
var position = this.object.position;
var offset = position.clone().sub( this.target );
// angle from z-axis around y-axis
var theta = Math.atan2( offset.x, offset.z );
// angle from y-axis
var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y );
if ( this.autoRotate ) {
this.rotateLeft( getAutoRotationAngle() );
}
theta += thetaDelta;
phi += phiDelta;
// restrict phi to be between desired limits
phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) );
// restrict phi to be betwee EPS and PI-EPS
phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) );
var radius = offset.length() * scale;
// restrict radius to be between desired limits
radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) );
// move target to panned location
this.target.add( pan );
offset.x = radius * Math.sin( phi ) * Math.sin( theta );
offset.y = radius * Math.cos( phi );
offset.z = radius * Math.sin( phi ) * Math.cos( theta );
position.copy( this.target ).add( offset );
this.object.lookAt( this.target );
thetaDelta = 0;
phiDelta = 0;
scale = 1;
pan.set(0,0,0);
if ( lastPosition.distanceTo( this.object.position ) > 0 ) {
this.dispatchEvent( changeEvent );
lastPosition.copy( this.object.position );
}
};
function getAutoRotationAngle() {
return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
}
function getZoomScale() {
return Math.pow( 0.95, scope.zoomSpeed );
}
function onMouseDown( event ) {
if ( scope.enabled === false ) { return; }
event.preventDefault();
if ( event.button === 0 ) {
if ( scope.noRotate === true ) { return; }
state = STATE.ROTATE;
rotateStart.set( event.clientX, event.clientY );
} else if ( event.button === 1 ) {
if ( scope.noZoom === true ) { return; }
state = STATE.DOLLY;
dollyStart.set( event.clientX, event.clientY );
} else if ( event.button === 2 ) {
if ( scope.noPan === true ) { return; }
state = STATE.PAN;
panStart.set( event.clientX, event.clientY );
}
// Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be
scope.domElement.addEventListener( 'mousemove', onMouseMove, false );
scope.domElement.addEventListener( 'mouseup', onMouseUp, false );
}
function onMouseMove( event ) {
if ( scope.enabled === false ) return;
event.preventDefault();
var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
if ( state === STATE.ROTATE ) {
if ( scope.noRotate === true ) return;
rotateEnd.set( event.clientX, event.clientY );
rotateDelta.subVectors( rotateEnd, rotateStart );
// rotating across whole screen goes 360 degrees around
scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
// rotating up and down along whole screen attempts to go 360, but limited to 180
scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
rotateStart.copy( rotateEnd );
} else if ( state === STATE.DOLLY ) {
if ( scope.noZoom === true ) return;
dollyEnd.set( event.clientX, event.clientY );
dollyDelta.subVectors( dollyEnd, dollyStart );
if ( dollyDelta.y > 0 ) {
scope.dollyIn();
} else {
scope.dollyOut();
}
dollyStart.copy( dollyEnd );
} else if ( state === STATE.PAN ) {
if ( scope.noPan === true ) return;
panEnd.set( event.clientX, event.clientY );
panDelta.subVectors( panEnd, panStart );
scope.pan( panDelta );
panStart.copy( panEnd );
}
// Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be
scope.update();
}
function onMouseUp( /* event */ ) {
if ( scope.enabled === false ) return;
// Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be
scope.domElement.removeEventListener( 'mousemove', onMouseMove, false );
scope.domElement.removeEventListener( 'mouseup', onMouseUp, false );
state = STATE.NONE;
}
function onMouseWheel( event ) {
if ( scope.enabled === false || scope.noZoom === true ) return;
var delta = 0;
if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9
delta = event.wheelDelta;
} else if ( event.detail ) { // Firefox
delta = - event.detail;
}
if ( delta > 0 ) {
scope.dollyOut();
} else {
scope.dollyIn();
}
}
function onKeyDown( event ) {
if ( scope.enabled === false ) { return; }
if ( scope.noKeys === true ) { return; }
if ( scope.noPan === true ) { return; }
// pan a pixel - I guess for precise positioning?
// Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be
var needUpdate = false;
switch ( event.keyCode ) {
case scope.keys.UP:
scope.pan( new THREE.Vector2( 0, scope.keyPanSpeed ) );
needUpdate = true;
break;
case scope.keys.BOTTOM:
scope.pan( new THREE.Vector2( 0, -scope.keyPanSpeed ) );
needUpdate = true;
break;
case scope.keys.LEFT:
scope.pan( new THREE.Vector2( scope.keyPanSpeed, 0 ) );
needUpdate = true;
break;
case scope.keys.RIGHT:
scope.pan( new THREE.Vector2( -scope.keyPanSpeed, 0 ) );
needUpdate = true;
break;
}
// Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be
if ( needUpdate ) {
scope.update();
}
}
function touchstart( event ) {
if ( scope.enabled === false ) { return; }
switch ( event.touches.length ) {
case 1: // one-fingered touch: rotate
if ( scope.noRotate === true ) { return; }
state = STATE.TOUCH_ROTATE;
rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
break;
case 2: // two-fingered touch: dolly
if ( scope.noZoom === true ) { return; }
state = STATE.TOUCH_DOLLY;
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
var distance = Math.sqrt( dx * dx + dy * dy );
dollyStart.set( 0, distance );
break;
case 3: // three-fingered touch: pan
if ( scope.noPan === true ) { return; }
state = STATE.TOUCH_PAN;
panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
break;
default:
state = STATE.NONE;
}
}
function touchmove( event ) {
if ( scope.enabled === false ) { return; }
event.preventDefault();
event.stopPropagation();
var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
switch ( event.touches.length ) {
case 1: // one-fingered touch: rotate
if ( scope.noRotate === true ) { return; }
if ( state !== STATE.TOUCH_ROTATE ) { return; }
rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
rotateDelta.subVectors( rotateEnd, rotateStart );
// rotating across whole screen goes 360 degrees around
scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
// rotating up and down along whole screen attempts to go 360, but limited to 180
scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
rotateStart.copy( rotateEnd );
break;
case 2: // two-fingered touch: dolly
if ( scope.noZoom === true ) { return; }
if ( state !== STATE.TOUCH_DOLLY ) { return; }
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
var distance = Math.sqrt( dx * dx + dy * dy );
dollyEnd.set( 0, distance );
dollyDelta.subVectors( dollyEnd, dollyStart );
if ( dollyDelta.y > 0 ) {
scope.dollyOut();
} else {
scope.dollyIn();
}
dollyStart.copy( dollyEnd );
break;
case 3: // three-fingered touch: pan
if ( scope.noPan === true ) { return; }
if ( state !== STATE.TOUCH_PAN ) { return; }
panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
panDelta.subVectors( panEnd, panStart );
scope.pan( panDelta );
panStart.copy( panEnd );
break;
default:
state = STATE.NONE;
}
}
function touchend( /* event */ ) {
if ( scope.enabled === false ) { return; }
state = STATE.NONE;
}
this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
this.localElement.addEventListener( 'mousedown', onMouseDown, false );
this.domElement.addEventListener( 'mousewheel', onMouseWheel, false );
this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox
this.domElement.addEventListener( 'keydown', onKeyDown, false );
this.localElement.addEventListener( 'touchstart', touchstart, false );
this.domElement.addEventListener( 'touchend', touchend, false );
this.domElement.addEventListener( 'touchmove', touchmove, false );
};
THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );
This Pen doesn't use any external CSS resources.