From f145384117a1ade0dcd476034a3bdaf75f16b1cd Mon Sep 17 00:00:00 2001 From: yushijinhun Date: Thu, 31 Aug 2017 21:32:45 +0800 Subject: [PATCH 1/3] disable rotating --- js/skinpreview3d.js | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/js/skinpreview3d.js b/js/skinpreview3d.js index 6597c7c..8d208c5 100644 --- a/js/skinpreview3d.js +++ b/js/skinpreview3d.js @@ -70,14 +70,12 @@ function SkinUtils(){ function SkinPreview3D(model, canvasW, canvasH, isSlim){ var radius = 32; var isPaused = false; - var originMouseX = 0; var rotating = false; - var modelRot = 0; var angleRot = 0; - var mouseDown = false; var camera = new THREE.PerspectiveCamera(75, canvasW / canvasH, 1, 10000); camera.position.y = -12; + camera.position.z = radius; var scene = new THREE.Scene(); var utils = new SkinUtils(); @@ -452,16 +450,8 @@ function SkinPreview3D(model, canvasW, canvasH, isSlim){ var drawSkin = () => { requestAnimationFrame(drawSkin); var time = (Date.now() - startTime)/1000; - if(!mouseDown && !isPaused){ - modelRot += 0.5; + if(!isPaused) angleRot += 0.01; - } - - var ang = -(modelRot * Math.PI / 180); - - camera.rotation.y = ang; - camera.position.z = radius*Math.cos(ang); - camera.position.x = radius*Math.sin(ang); var speed = 3; //Leg Swing @@ -486,21 +476,8 @@ function SkinPreview3D(model, canvasW, canvasH, isSlim){ angleRot = 0; } - model.mousedown(function(e){ - originMouseX = (e.pageX - this.offsetLeft) - modelRot; - mouseDown = true; - }); - - window.jQuery(document).mouseup(() => mouseDown = false); - model.bind('contextmenu', e => { e.preventDefault(); isPaused = !isPaused; }); - - model.mousemove(function(e){ - if(!mouseDown) return; - var x = (e.pageX - this.offsetLeft) - originMouseX; - modelRot = x; - }); } From 4af41f72a1cfeeed6263c1837dca3bc37f913973 Mon Sep 17 00:00:00 2001 From: yushijinhun Date: Fri, 1 Sep 2017 21:57:50 +0800 Subject: [PATCH 2/3] Move things into namespace 'skinpreview3d' --- example.html | 2 +- js/skinpreview3d.js | 830 +++++++++++++++++++++++--------------------- 2 files changed, 426 insertions(+), 406 deletions(-) diff --git a/example.html b/example.html index a2c09cf..71bc7d9 100644 --- a/example.html +++ b/example.html @@ -13,7 +13,7 @@ + + + + diff --git a/js/skinpreview3d.js b/js/skinpreview3d.js index 5b7f35b..70629b9 100644 --- a/js/skinpreview3d.js +++ b/js/skinpreview3d.js @@ -84,16 +84,10 @@ var skinpreview3d = new function(){ box.faceVertexUvs[0][11] = [back[0], back[1], back[2]]; }; - this.SkinViewer = function(model, canvasW, canvasH, isSlim){ + this.SkinViewer = function(domElement, canvasW, canvasH, isSlim){ var angleRot = 0; var initialized = false; - var camera = new THREE.PerspectiveCamera(75, canvasW / canvasH, 1, 10000); - camera.position.y = -12; - camera.position.z = 32; - - var scene = new THREE.Scene(); - var skinImg = new Image(); var skinCanvas = document.createElement('canvas'); var skinContext = skinCanvas.getContext('2d'); @@ -112,7 +106,6 @@ var skinpreview3d = new function(){ var layer2Material = new THREE.MeshBasicMaterial({map: skinTexture, transparent: true, opacity: 1, side: THREE.DoubleSide}); var capeMaterial = new THREE.MeshBasicMaterial({map: capeTexture}); - var renderer; var capePivot; var headBox, headMesh, @@ -129,10 +122,30 @@ var skinpreview3d = new function(){ leftLeg2Box, leftLeg2Mesh, capeBox, capeMesh; - this.paused = false; + this.domElement = domElement; + this.animationPaused = false; this.animationSpeed = 3; - this.setSkin = url => skinImg.src = url; - this.setCape = url => capeImg.src = url; + + this.scene = new THREE.Scene(); + + this.camera = new THREE.PerspectiveCamera(75, canvasW / canvasH, 1, 10000); + this.camera.position.y = -12; + this.camera.position.z = 30; + + this.renderer = new THREE.WebGLRenderer({angleRot: true, alpha: true, antialias: false}); + this.renderer.setSize(canvasW, canvasH); + this.renderer.context.getShaderInfoLog = () => ''; // shut firefox up + + Object.defineProperties(this, { + 'skinUrl': { + get: () => skinImg.src, + set: url => skinImg.src = url + }, + 'capeUrl': { + get: () => capeImg.src, + set: url => capeImg.src = url + } + }); var initialize = () => { // Head Parts @@ -147,7 +160,7 @@ var skinpreview3d = new function(){ ); headMesh = new THREE.Mesh(headBox, layer1Material); headMesh.name = 'head'; - scene.add(headMesh); + this.scene.add(headMesh); // Body Parts bodyBox = new THREE.BoxGeometry(8, 12, 4, 0, 0, 0); @@ -162,7 +175,7 @@ var skinpreview3d = new function(){ bodyMesh = new THREE.Mesh(bodyBox, layer1Material); bodyMesh.name = 'body'; bodyMesh.position.y = -10; - scene.add(bodyMesh); + this.scene.add(bodyMesh); // Right Arm Parts rightArmBox = new THREE.BoxGeometry(isSlim?3:4, 12, 4, 0, 0, 0); @@ -189,7 +202,7 @@ var skinpreview3d = new function(){ rightArmMesh.name = 'rightArm'; rightArmMesh.position.y = -10; rightArmMesh.position.x = isSlim?-5.5:-6; - scene.add(rightArmMesh); + this.scene.add(rightArmMesh); // Left Arm Parts leftArmBox = new THREE.BoxGeometry(isSlim?3:4, 12, 4, 0, 0, 0); @@ -216,7 +229,7 @@ var skinpreview3d = new function(){ leftArmMesh.name = 'leftArm'; leftArmMesh.position.y = -10; leftArmMesh.position.x = isSlim?5.5:6; - scene.add(leftArmMesh); + this.scene.add(leftArmMesh); // Right Leg Parts rightLegBox = new THREE.BoxGeometry(4, 12, 4, 0, 0, 0); @@ -232,7 +245,7 @@ var skinpreview3d = new function(){ rightLegMesh.name = 'rightLeg' rightLegMesh.position.y = -22; rightLegMesh.position.x = -2; - scene.add(rightLegMesh); + this.scene.add(rightLegMesh); // Left Leg Parts leftLegBox = new THREE.BoxGeometry(4, 12, 4, 0, 0, 0); @@ -248,7 +261,7 @@ var skinpreview3d = new function(){ leftLegMesh.name = 'leftLeg'; leftLegMesh.position.y = -22; leftLegMesh.position.x = 2; - scene.add(leftLegMesh); + this.scene.add(leftLegMesh); // Head Overlay Parts head2Box = new THREE.BoxGeometry(9, 9, 9, 0, 0, 0); @@ -262,7 +275,7 @@ var skinpreview3d = new function(){ ); head2Mesh = new THREE.Mesh(head2Box, layer2Material); head2Mesh.name = 'head2' - scene.add(head2Mesh); + this.scene.add(head2Mesh); // Body Overlay Parts body2Box = new THREE.BoxGeometry(9, 13.5, 4.5, 0, 0, 0); @@ -277,7 +290,7 @@ var skinpreview3d = new function(){ body2Mesh = new THREE.Mesh(body2Box, layer2Material); body2Mesh.name = 'body2'; body2Mesh.position.y = -10; - scene.add(body2Mesh); + this.scene.add(body2Mesh); // Right Arm Overlay Parts rightArm2Box = new THREE.BoxGeometry(isSlim?3.375:4.5, 13.5, 4.5, 0, 0, 0); @@ -304,7 +317,7 @@ var skinpreview3d = new function(){ rightArm2Mesh.name = 'rightArm2'; rightArm2Mesh.position.y = -10; rightArm2Mesh.position.x = -6; - scene.add(rightArm2Mesh); + this.scene.add(rightArm2Mesh); // Left Arm Overlay Parts leftArm2Box = new THREE.BoxGeometry(isSlim?3.375:4.5, 13.5, 4.5, 0, 0, 0); @@ -332,7 +345,7 @@ var skinpreview3d = new function(){ leftArm2Mesh.position.y = -10; leftArm2Mesh.position.x = 6; // leftArm2Mesh.visible = true; - scene.add(leftArm2Mesh); + this.scene.add(leftArm2Mesh); // Right Leg Overlay Parts rightLeg2Box = new THREE.BoxGeometry(4.5, 13.5, 4.5, 0, 0, 0); @@ -348,7 +361,7 @@ var skinpreview3d = new function(){ rightLeg2Mesh.name = 'rightLeg2' rightLeg2Mesh.position.y = -22; rightLeg2Mesh.position.x = -2; - scene.add(rightLeg2Mesh); + this.scene.add(rightLeg2Mesh); // Left Leg Overlay Parts leftLeg2Box = new THREE.BoxGeometry(4.5, 13.5, 4.5, 0, 0, 0); @@ -364,7 +377,7 @@ var skinpreview3d = new function(){ leftLeg2Mesh.name = 'leftLeg2'; leftLeg2Mesh.position.y = -22; leftLeg2Mesh.position.x = 2; - scene.add(leftLeg2Mesh); + this.scene.add(leftLeg2Mesh); // Cape Parts // back = outside @@ -384,13 +397,9 @@ var skinpreview3d = new function(){ capeMesh.position.z = -0.55; capePivot = new THREE.Group(); capePivot.rotation.x = 25 * (Math.PI/180); - scene.add(capePivot); + this.scene.add(capePivot); - renderer = new THREE.WebGLRenderer({angleRot: true, alpha: true, antialias: false}); - renderer.setSize(canvasW, canvasH); - renderer.context.getShaderInfoLog = () => ''; // shut firefox up - - model.append(renderer.domElement); + this.domElement.append(this.renderer.domElement); } skinImg.crossOrigin = ''; @@ -454,7 +463,7 @@ var skinpreview3d = new function(){ var drawSkin = () => { requestAnimationFrame(drawSkin); var time = (Date.now() - startTime)/1000; - if(!this.paused) + if(!this.animationPaused) angleRot += 0.01; //Leg Swing @@ -473,31 +482,728 @@ var skinpreview3d = new function(){ rightArm2Mesh.position.z = rightArmMesh.position.z = 0 - 6*Math.sin(rightArmMesh.rotation.x); rightArm2Mesh.position.y = rightArmMesh.position.y = -4 - 6*Math.abs(Math.cos(rightArmMesh.rotation.x)); - renderer.render(scene, camera); + this.renderer.render(this.scene, this.camera); if(angleRot > 360) angleRot = 0; } } + // ====== OrbitControls ====== + // The code was originally from https://github.com/mrdoob/three.js/blob/d45a042cf962e9b1aa9441810ba118647b48aacb/examples/js/controls/OrbitControls.js + /** + * The MIT License + * + * Copyright © 2010-2017 three.js authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + this.OrbitControls = function (object, domElement) { + /** + * @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 + */ + // This set of controls performs orbiting, dollying (zooming), and panning. + // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default). + // + // 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 finger swipe + + this.object = object; + + this.domElement = (domElement !== undefined) ? domElement : document; + + // Set to false to disable this control + this.enabled = true; + + // "target" sets the location of focus, where the object orbits around + this.target = new THREE.Vector3(); + + // How far you can dolly in and out (PerspectiveCamera only) + this.minDistance = 0; + this.maxDistance = Infinity; + + // How far you can zoom in and out (OrthographicCamera only) + this.minZoom = 0; + this.maxZoom = Infinity; + + // 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 + + // How far you can orbit horizontally, upper and lower limits. + // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ]. + this.minAzimuthAngle = - Infinity; // radians + this.maxAzimuthAngle = Infinity; // radians + + // Set to true to enable damping (inertia) + // If damping is enabled, you must call controls.update() in your animation loop + this.enableDamping = false; + this.dampingFactor = 0.25; + + // This option actually enables dollying in and out; left as "zoom" for backwards compatibility. + // Set to false to disable zooming + this.enableZoom = true; + this.zoomSpeed = 1.0; + + // Set to false to disable rotating + this.enableRotate = true; + this.rotateSpeed = 1.0; + + // Set to false to disable panning + this.enablePan = true; + this.keyPanSpeed = 7.0; // pixels moved per arrow key push + + // Set to true to automatically rotate around the target + // If auto-rotate is enabled, you must call controls.update() in your animation loop + this.autoRotate = false; + this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 + + // Set to false to disable use of the keys + this.enableKeys = true; + + // The four arrow keys + this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; + + // Mouse buttons + this.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT }; + + // for reset + this.target0 = this.target.clone(); + this.position0 = this.object.position.clone(); + this.zoom0 = this.object.zoom; + + // + // public methods + // + + this.getPolarAngle = function () { + return spherical.phi; + }; + + this.getAzimuthalAngle = function () { + return spherical.theta; + }; + + this.saveState = function () { + scope.target0.copy(scope.target); + scope.position0.copy(scope.object.position); + scope.zoom0 = scope.object.zoom; + }; + + this.reset = function () { + scope.target.copy(scope.target0); + scope.object.position.copy(scope.position0); + scope.object.zoom = scope.zoom0; + + scope.object.updateProjectionMatrix(); + scope.dispatchEvent(changeEvent); + + scope.update(); + + state = STATE.NONE; + }; + + // this method is exposed, but perhaps it would be better if we can make it private... + this.update = function () { + var offset = new THREE.Vector3(); + + // so camera.up is the orbit axis + var quat = new THREE.Quaternion().setFromUnitVectors(object.up, new THREE.Vector3(0, 1, 0)); + var quatInverse = quat.clone().inverse(); + + var lastPosition = new THREE.Vector3(); + var lastQuaternion = new THREE.Quaternion(); + + return function update() { + + var position = scope.object.position; + + offset.copy(position).sub(scope.target); + + // rotate offset to "y-axis-is-up" space + offset.applyQuaternion(quat); + + // angle from z-axis around y-axis + spherical.setFromVector3(offset); + + if (scope.autoRotate && state === STATE.NONE) { + + rotateLeft(getAutoRotationAngle()); + + } + + spherical.theta += sphericalDelta.theta; + spherical.phi += sphericalDelta.phi; + + // restrict theta to be between desired limits + spherical.theta = Math.max(scope.minAzimuthAngle, Math.min(scope.maxAzimuthAngle, spherical.theta)); + + // restrict phi to be between desired limits + spherical.phi = Math.max(scope.minPolarAngle, Math.min(scope.maxPolarAngle, spherical.phi)); + + spherical.makeSafe(); + + spherical.radius *= scale; + + // restrict radius to be between desired limits + spherical.radius = Math.max(scope.minDistance, Math.min(scope.maxDistance, spherical.radius)); + + // move target to panned location + scope.target.add(panOffset); + + offset.setFromSpherical(spherical); + + // rotate offset back to "camera-up-vector-is-up" space + offset.applyQuaternion(quatInverse); + + position.copy(scope.target).add(offset); + + scope.object.lookAt(scope.target); + + if (scope.enableDamping === true) { + + sphericalDelta.theta *= (1 - scope.dampingFactor); + sphericalDelta.phi *= (1 - scope.dampingFactor); + + } else { + + sphericalDelta.set(0, 0, 0); + + } + + scale = 1; + panOffset.set(0, 0, 0); + + // update condition is: + // min(camera displacement, camera rotation in radians)^2 > EPS + // using small-angle approximation cos(x/2) = 1 - x^2 / 8 + + if (zoomChanged || + lastPosition.distanceToSquared(scope.object.position) > EPS || + 8 * (1 - lastQuaternion.dot(scope.object.quaternion)) > EPS) { + + scope.dispatchEvent(changeEvent); + + lastPosition.copy(scope.object.position); + lastQuaternion.copy(scope.object.quaternion); + zoomChanged = false; + + return true; + } + return false; + }; + }(); + + this.dispose = function () { + scope.domElement.removeEventListener('contextmenu', onContextMenu, false); + scope.domElement.removeEventListener('mousedown', onMouseDown, false); + scope.domElement.removeEventListener('wheel', onMouseWheel, false); + + scope.domElement.removeEventListener('touchstart', onTouchStart, false); + scope.domElement.removeEventListener('touchend', onTouchEnd, false); + scope.domElement.removeEventListener('touchmove', onTouchMove, false); + + document.removeEventListener('mousemove', onMouseMove, false); + document.removeEventListener('mouseup', onMouseUp, false); + + window.removeEventListener('keydown', onKeyDown, false); + + //scope.dispatchEvent({ type: 'dispose' }); // should this be added here? + }; + + // + // internals + // + + var scope = this; + + var changeEvent = { type: 'change' }; + var startEvent = { type: 'start' }; + var endEvent = { type: 'end' }; + + var STATE = { NONE: - 1, ROTATE: 0, DOLLY: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_DOLLY: 4, TOUCH_PAN: 5 }; + + var state = STATE.NONE; + + var EPS = 0.000001; + + // current position in spherical coordinates + var spherical = new THREE.Spherical(); + var sphericalDelta = new THREE.Spherical(); + + var scale = 1; + var panOffset = new THREE.Vector3(); + var zoomChanged = false; + + 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(); + + function getAutoRotationAngle() { + return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; + } + + function getZoomScale() { + return Math.pow(0.95, scope.zoomSpeed); + } + + function rotateLeft(angle) { + sphericalDelta.theta -= angle; + } + + function rotateUp(angle) { + sphericalDelta.phi -= angle; + } + + var panLeft = function () { + var v = new THREE.Vector3(); + return function panLeft(distance, objectMatrix) { + v.setFromMatrixColumn(objectMatrix, 0); // get X column of objectMatrix + v.multiplyScalar(- distance); + + panOffset.add(v); + }; + }(); + + var panUp = function () { + var v = new THREE.Vector3(); + return function panUp(distance, objectMatrix) { + v.setFromMatrixColumn(objectMatrix, 1); // get Y column of objectMatrix + v.multiplyScalar(distance); + + panOffset.add(v); + }; + }(); + + // deltaX and deltaY are in pixels; right and down are positive + var pan = function () { + var offset = new THREE.Vector3(); + return function pan(deltaX, deltaY) { + var element = scope.domElement === document ? scope.domElement.body : scope.domElement; + if (scope.object instanceof THREE.PerspectiveCamera) { + // perspective + var position = scope.object.position; + offset.copy(position).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 + panLeft(2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix); + panUp(2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix); + } else if (scope.object instanceof THREE.OrthographicCamera) { + // orthographic + panLeft(deltaX * (scope.object.right - scope.object.left) / scope.object.zoom / element.clientWidth, scope.object.matrix); + panUp(deltaY * (scope.object.top - scope.object.bottom) / scope.object.zoom / element.clientHeight, scope.object.matrix); + } else { + // camera neither orthographic nor perspective + console.warn('WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.'); + scope.enablePan = false; + } + }; + }(); + + function dollyIn(dollyScale) { + if (scope.object instanceof THREE.PerspectiveCamera) { + scale /= dollyScale; + } else if (scope.object instanceof THREE.OrthographicCamera) { + scope.object.zoom = Math.max(scope.minZoom, Math.min(scope.maxZoom, scope.object.zoom * dollyScale)); + scope.object.updateProjectionMatrix(); + zoomChanged = true; + } else { + console.warn('WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.'); + scope.enableZoom = false; + } + } + + function dollyOut(dollyScale) { + if (scope.object instanceof THREE.PerspectiveCamera) { + scale *= dollyScale; + } else if (scope.object instanceof THREE.OrthographicCamera) { + scope.object.zoom = Math.max(scope.minZoom, Math.min(scope.maxZoom, scope.object.zoom / dollyScale)); + scope.object.updateProjectionMatrix(); + zoomChanged = true; + } else { + console.warn('WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.'); + scope.enableZoom = false; + } + } + + // + // event callbacks - update the object state + // + + function handleMouseDownRotate(event) { + rotateStart.set(event.clientX, event.clientY); + + } + + function handleMouseDownDolly(event) { + dollyStart.set(event.clientX, event.clientY); + + } + + function handleMouseDownPan(event) { + panStart.set(event.clientX, event.clientY); + + } + + function handleMouseMoveRotate(event) { + rotateEnd.set(event.clientX, event.clientY); + rotateDelta.subVectors(rotateEnd, rotateStart); + var element = scope.domElement === document ? scope.domElement.body : scope.domElement; + // rotating across whole screen goes 360 degrees around + 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 + rotateUp(2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed); + rotateStart.copy(rotateEnd); + scope.update(); + } + + function handleMouseMoveDolly(event) { + dollyEnd.set(event.clientX, event.clientY); + dollyDelta.subVectors(dollyEnd, dollyStart); + if (dollyDelta.y > 0) { + dollyIn(getZoomScale()); + } else if (dollyDelta.y < 0) { + dollyOut(getZoomScale()); + } + dollyStart.copy(dollyEnd); + scope.update(); + } + + function handleMouseMovePan(event) { + panEnd.set(event.clientX, event.clientY); + panDelta.subVectors(panEnd, panStart); + pan(panDelta.x, panDelta.y); + panStart.copy(panEnd); + scope.update(); + } + + function handleMouseUp(event) { + } + + function handleMouseWheel(event) { + if (event.deltaY < 0) { + dollyOut(getZoomScale()); + } else if (event.deltaY > 0) { + dollyIn(getZoomScale()); + } + scope.update(); + } + + function handleKeyDown(event) { + switch (event.keyCode) { + case scope.keys.UP: + pan(0, scope.keyPanSpeed); + scope.update(); + break; + + case scope.keys.BOTTOM: + pan(0, - scope.keyPanSpeed); + scope.update(); + break; + + case scope.keys.LEFT: + pan(scope.keyPanSpeed, 0); + scope.update(); + break; + + case scope.keys.RIGHT: + pan(- scope.keyPanSpeed, 0); + scope.update(); + break; + } + + } + + function handleTouchStartRotate(event) { + rotateStart.set(event.touches[ 0 ].pageX, event.touches[ 0 ].pageY); + + } + + function handleTouchStartDolly(event) { + 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); + } + + function handleTouchStartPan(event) { + panStart.set(event.touches[ 0 ].pageX, event.touches[ 0 ].pageY); + } + + function handleTouchMoveRotate(event) { + rotateEnd.set(event.touches[ 0 ].pageX, event.touches[ 0 ].pageY); + rotateDelta.subVectors(rotateEnd, rotateStart); + var element = scope.domElement === document ? scope.domElement.body : scope.domElement; + rotateLeft(2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed); + rotateUp(2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed); + rotateStart.copy(rotateEnd); + scope.update(); + + } + + function handleTouchMoveDolly(event) { + 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) { + dollyOut(getZoomScale()); + } else if (dollyDelta.y < 0) { + dollyIn(getZoomScale()); + } + dollyStart.copy(dollyEnd); + scope.update(); + + } + + function handleTouchMovePan(event) { + panEnd.set(event.touches[ 0 ].pageX, event.touches[ 0 ].pageY); + panDelta.subVectors(panEnd, panStart); + pan(panDelta.x, panDelta.y); + panStart.copy(panEnd); + scope.update(); + } + + function handleTouchEnd(event) { + } + + // + // event handlers - FSM: listen for events and reset state + // + + function onMouseDown(event) { + if (scope.enabled === false) return; + switch (event.button) { + case scope.mouseButtons.ORBIT: + if (scope.enableRotate === false) return; + handleMouseDownRotate(event); + state = STATE.ROTATE; + break; + + case scope.mouseButtons.ZOOM: + if (scope.enableZoom === false) return; + handleMouseDownDolly(event); + state = STATE.DOLLY; + break; + + case scope.mouseButtons.PAN: + if (scope.enablePan === false) return; + handleMouseDownPan(event); + state = STATE.PAN; + break; + } + + event.preventDefault(); + if (state !== STATE.NONE) { + document.addEventListener('mousemove', onMouseMove, false); + document.addEventListener('mouseup', onMouseUp, false); + scope.dispatchEvent(startEvent); + } + } + + function onMouseMove(event) { + if (scope.enabled === false) return; + switch (state) { + case STATE.ROTATE: + if (scope.enableRotate === false) return; + handleMouseMoveRotate(event); + break; + + case STATE.DOLLY: + if (scope.enableZoom === false) return; + handleMouseMoveDolly(event); + break; + + case STATE.PAN: + if (scope.enablePan === false) return; + handleMouseMovePan(event); + break; + } + event.preventDefault(); + } + + function onMouseUp(event) { + if (scope.enabled === false) return; + handleMouseUp(event); + document.removeEventListener('mousemove', onMouseMove, false); + document.removeEventListener('mouseup', onMouseUp, false); + scope.dispatchEvent(endEvent); + state = STATE.NONE; + } + + function onMouseWheel(event) { + if (scope.enabled === false || scope.enableZoom === false || (state !== STATE.NONE && state !== STATE.ROTATE)) return; + event.preventDefault(); + event.stopPropagation(); + handleMouseWheel(event); + scope.dispatchEvent(startEvent); // not sure why these are here... + scope.dispatchEvent(endEvent); + } + + function onKeyDown(event) { + if (scope.enabled === false || scope.enableKeys === false || scope.enablePan === false) return; + handleKeyDown(event); + } + + function onTouchStart(event) { + if (scope.enabled === false) return; + switch (event.touches.length) { + case 1: // one-fingered touch: rotate + if (scope.enableRotate === false) return; + handleTouchStartRotate(event); + state = STATE.TOUCH_ROTATE; + break; + + case 2: // two-fingered touch: dolly + if (scope.enableZoom === false) return; + handleTouchStartDolly(event); + state = STATE.TOUCH_DOLLY; + break; + + case 3: // three-fingered touch: pan + if (scope.enablePan === false) return; + handleTouchStartPan(event); + state = STATE.TOUCH_PAN; + break; + + default: + state = STATE.NONE; + } + + if (state !== STATE.NONE) { + scope.dispatchEvent(startEvent); + } + + } + + function onTouchMove(event) { + if (scope.enabled === false) return; + switch (event.touches.length) { + case 1: // one-fingered touch: rotate + if (scope.enableRotate === false) return; + if (state !== STATE.TOUCH_ROTATE) return; // is this needed?... + handleTouchMoveRotate(event); + break; + + case 2: // two-fingered touch: dolly + if (scope.enableZoom === false) return; + if (state !== STATE.TOUCH_DOLLY) return; // is this needed?... + handleTouchMoveDolly(event); + break; + + case 3: // three-fingered touch: pan + if (scope.enablePan === false) return; + if (state !== STATE.TOUCH_PAN) return; // is this needed?... + handleTouchMovePan(event); + break; + + default: + state = STATE.NONE; + } + event.preventDefault(); + event.stopPropagation(); + } + + function onTouchEnd(event) { + if (scope.enabled === false) return; + handleTouchEnd(event); + scope.dispatchEvent(endEvent); + state = STATE.NONE; + } + + function onContextMenu(event) { + if (scope.enabled === false || scope.enablePan === false) return; + event.preventDefault(); + } + + // + + scope.domElement.addEventListener('contextmenu', onContextMenu, false); + + scope.domElement.addEventListener('mousedown', onMouseDown, false); + scope.domElement.addEventListener('wheel', onMouseWheel, false); + + scope.domElement.addEventListener('touchstart', onTouchStart, false); + scope.domElement.addEventListener('touchend', onTouchEnd, false); + scope.domElement.addEventListener('touchmove', onTouchMove, false); + + window.addEventListener('keydown', onKeyDown, false); + + // force an update at start + + this.update(); + }; + + this.OrbitControls.prototype = Object.create(THREE.EventDispatcher.prototype); + this.OrbitControls.prototype.constructor = this.OrbitControls; + // ============ + + this.SkinControl = function(skinViewer) { + this.enableAnimationControl = true; + this.skinViewer = skinViewer; + + this.orbitControls = new skinpreview3d.OrbitControls(skinViewer.camera, skinViewer.renderer.domElement); + this.orbitControls.enablePan = false; + this.orbitControls.target = new THREE.Vector3(0, -12 ,0); + this.orbitControls.minDistance = 10; + this.orbitControls.maxDistance = 256; + this.orbitControls.update(); + + this.skinViewer.domElement.addEventListener('contextmenu', e => { + if(this.enableAnimationControl) { + e.preventDefault(); + this.skinViewer.animationPaused = !this.skinViewer.animationPaused; + } + }, false); + } + } if(window.jQuery){ (function($) { $.fn.skinPreview3d = function (options) { - var sp = new skinpreview3d.SkinViewer(this, options.width, options.height, options.slim === true); - sp.setSkin(options.skinUrl); + var skinViewer = new skinpreview3d.SkinViewer(this.get(0), options.width, options.height, options.slim === true); + skinViewer.skinUrl = options.skinUrl; if(options.capeUrl != null) - sp.setCape(options.capeUrl); + skinViewer.capeUrl = options.capeUrl; - if(options.disableControl!==true){ - this.bind('contextmenu', e => { - e.preventDefault(); - sp.paused = !sp.paused; - }); - } - - return sp; + if(options.disableControl !== true) + skinViewer.control = new skinpreview3d.SkinControl(skinViewer); }; } (window.jQuery)); }