// Last updated 06/06/05 // // The standard Holomatix/Blaze navigation paradigm, suitable for general applications, including 360-degree 'all-around the product' presentations. // For use in Blaze 3D Studio 1.5 onwards // // To add normal mouse navigation to your Blaze 3D project, simply paste the below code into your renderWindow MoveClip // and modify the top 4 variable values as desired onClipEvent (load) { ZOOM_MINDIST = 10; //closest the camera can get to the origin ZOOM_MAXDIST = 40; //furthest the camera can get from the origin stageWidth = 400; //The stage dimensions values are used by the mouse pan control stageHeight = 340; // Where the render window does not have the same dimensions as the stage then you MUST modify the min and max values of the x & y coords // to ensure that the model does not disappear from the users view. minXLimit = 0.0; //look at the start x position point of the render window maxXLimit = stageWidth; //look at the end x position point of the render window minYLimit = 0.0; //look at the start y position point of the render window maxYLimit = stageHeight;//look at the start y position point of the render window xProjectionCentre = this._x; yProjectionCentre = this._y; omousex = _root._xmouse; omousey = _root._ymouse; //mouse velocities velx = 0; vely = 0; //control panel velocities xRot = 0; yRot = 0; xPan = 0; yPan = 0; zoom = 0; xRotMax = 10; yRotMax = 10; velocityDecayRate = Math.exp(0.17); //it's the velocity decay rate - don't set to zero rotatePoint = new Vector3d(); mouseState = 0; STOP_STATE = 0; DRAG_STATE = 1; DECAY_STATE = 2; PANELDRAG_STATE = 3; PANELDECAY_STATE = 4; movementState = STOP_STATE; ROTATE_MOVETYPE = 1; ZOOM_MOVETYPE = 2; PAN_MOVETYPE = 3; movementType = 0; ROTXU_PANELBUTTON = 0; ROTXD_PANELBUTTON = 1; ROTYL_PANELBUTTON = 2; ROTYR_PANELBUTTON = 3; PANXL_PANELBUTTON = 4; PANXR_PANELBUTTON = 5; PANYU_PANELBUTTON = 6; PANYD_PANELBUTTON = 7; ZOOMIN_PANELBUTTON = 8; ZOOMOUT_PANELBUTTON = 9; ZOOM_PANELBUTTON = 10; panelButton = 0; buttonPressed = false; //Added this buttonPressed flag in case buttonPress and buttonRelease occur in same frame - always gotoposMatrix = new Matrix3d(); gotoposFrames = 0; gotoposActive = false; gotoposStart = false; allowNav = true; zoom_slider = false; // do not change. This variable should be changed when the slider first appears within the slider code radToDeg = 180/3.1415; degToRad = 1/radToDeg; } onClipEvent (mouseUp) { // update mouseState here whether in render window or not so we stop dragging mouseState = getMouseButtons(); if (movementState == DRAG_STATE && mouseState==0) movementState = DECAY_STATE; if (movementState == DRAG_STATE || movementState == STOP_STATE) { movementType = mouseState; } omousex = _root._xmouse; omousey = _root._ymouse; } onClipEvent (mouseDown) { if (this.hitTest(_root._xmouse, _root._ymouse, 0)) //mouse inside render window { mouseState = getMouseButtons(); if (mouseState!=0) //if mouseState == 0 after a mouseDown event, //it means that the event was handled by the flash player, so do nothing { //only stop if the movement is a different sort or we are interrupting panel decay if (mouseState != movementType || movementState == PANELDECAY_STATE) { movementType = mouseState; movementState = STOP_STATE; } omousex = _root._xmouse; omousey = _root._ymouse; } } } onClipEvent (mouseMove) { if (movementState == PANELDRAG_STATE || movementState == PANELDECAY_STATE) { mouseState = 0; } if (mouseState!=0) { movementType = mouseState; movementState = DRAG_STATE; } } onClipEvent (enterFrame) { cameraObj = getCamera(); if (gotoposStart) { gotoposMatrix = new Matrix3d(gotoposMatrix); //kill off any momentum movementState = STOP_STATE; //allow for not pointing at the origin //removing pan component //find the centre of rotation in the frame of the camera inverseMatrix = gotoposMatrix.inverse(); rotationPointInCameraFrame = inverseMatrix.multiply(rotatePoint); toDx = rotationPointInCameraFrame.x; toDy = rotationPointInCameraFrame.y; adx = toDx*gotoposMatrix.m00 + toDy*gotoposMatrix.m10; ady = toDx*gotoposMatrix.m01 + toDy*gotoposMatrix.m11; adz = toDx*gotoposMatrix.m02 + toDy*gotoposMatrix.m12; gotoposMatrix.translate(adx, ady, adz); //Store altered gotoposMatrix toVector = new Vector3d(gotoposMatrix.m30, gotoposMatrix.m31, gotoposMatrix.m32); toUpVector = new Vector3d(gotoposMatrix.m10, gotoposMatrix.m11, gotoposMatrix.m12); toAimVector = new Vector3d(gotoposMatrix.m20, gotoposMatrix.m21, gotoposMatrix.m22); toRadius = toVector.length(); toVector.normalize(); gotoposStart = false; gotoposActive = true; if (zoom_slider){ zoom_slider_path.current_z_distance = rotationPointInCameraFrame.z; zoom_slider_path.bZoomChanged = true; } } if (gotoposActive) { //05/03/04 check for gotoposFrames if (gotoposFrames<1) gotoposFrames = 1; fromMatrix = new Matrix3d(cameraObj.matrix); //Remove pan component //find the centre of rotation in the frame of the camera inverseMatrix = fromMatrix.inverse(); rotationPointInCameraFrame = inverseMatrix.multiply(rotatePoint); dx = rotationPointInCameraFrame.x; dy = rotationPointInCameraFrame.y; adx = dx*fromMatrix.m00 + dy*fromMatrix.m10; ady = dx*fromMatrix.m01 + dy*fromMatrix.m11; adz = dx*fromMatrix.m02 + dy*fromMatrix.m12; fromMatrix.translate(adx, ady, adz); //Now work on altered matrix fromVector = new Vector3d(fromMatrix.m30, fromMatrix.m31, fromMatrix.m32); fromUpVector = new Vector3d(fromMatrix.m10, fromMatrix.m11, fromMatrix.m12); fromAimVector = new Vector3d(fromMatrix.m20, fromMatrix.m21, fromMatrix.m22); fromRadius = fromVector.length(); //translation //get polar position dot = fromVector.dot(toVector); //sometimes came out as greater than fromRadius and caused problem in Math.acos function if (dot>fromRadius) dot = fromRadius; else if (dot<-fromRadius) dot = -fromRadius; translationPolarAngle = Math.acos(dot/fromRadius); //fromVector is not normalized //new polar position translationPolarAngle -= translationPolarAngle/gotoposFrames; radiusToGo = toRadius - fromRadius; fromRadius += radiusToGo/gotoposFrames; //set new translation outOfPlaneVector = fromVector.cross(toVector); thirdAxisVector = toVector.cross(outOfPlaneVector); thirdAxisVector.normalize(); cosAngle = Math.cos(translationPolarAngle); sinAngle = Math.sin(translationPolarAngle); fromMatrix.m30 = fromRadius*cosAngle*toVector.x + fromRadius*sinAngle*thirdAxisVector.x; fromMatrix.m31 = fromRadius*cosAngle*toVector.y + fromRadius*sinAngle*thirdAxisVector.y; fromMatrix.m32 = fromRadius*cosAngle*toVector.z + fromRadius*sinAngle*thirdAxisVector.z; //new aim vector... always point at origin fromAimVector.x = - fromMatrix.m30; fromAimVector.y = - fromMatrix.m31; fromAimVector.z = - fromMatrix.m32; fromAimVector.normalize(); fromMatrix.m20 = fromAimVector.x; fromMatrix.m21 = fromAimVector.y; fromMatrix.m22 = fromAimVector.z; //new up vector... fromUpVector.x = fromUpVector.x + (toUpVector.x - fromUpVector.x)/gotoposFrames; fromUpVector.y = fromUpVector.y + (toUpVector.y - fromUpVector.y)/gotoposFrames; fromUpVector.z = fromUpVector.z + (toUpVector.z - fromUpVector.z)/gotoposFrames; //x vector... xVector = fromUpVector.cross(fromAimVector); xVector.normalize(); fromMatrix.m00 = xVector.x; fromMatrix.m01 = xVector.y; fromMatrix.m02 = xVector.z; //actual up vector fromUpVector = fromAimVector.cross(xVector); fromUpVector.normalize(); fromMatrix.m10 = fromUpVector.x; fromMatrix.m11 = fromUpVector.y; fromMatrix.m12 = fromUpVector.z; //Now translate back to account for pan newDx = dx + (toDx - dx)/gotoposFrames; newDy = dy + (toDy - dy)/gotoposFrames; adx = -newDx*fromMatrix.m00 - newDy*fromMatrix.m10; ady = -newDx*fromMatrix.m01 - newDy*fromMatrix.m11; adz = -newDx*fromMatrix.m02 - newDy*fromMatrix.m12; fromMatrix.translate(adx, ady, adz); //set new camera matrix... cameraObj.matrix = fromMatrix; gotoposFrames -= 1; if (gotoposFrames==0) gotoposActive = false; if (zoom_slider){ zoom_slider_path.current_z_distance = rotationPointInCameraFrame.z; zoom_slider_path.bZoomChanged = true; } } if (movementState==STOP_STATE) { velx = 0; vely = 0; xPan = 0; yPan = 0; xRot = 0; yRot = 0; zoom = 0; } else if (allowNav) { if (buttonPressed || movementState==PANELDRAG_STATE) { xPanMax = stageWidth/30; yPanMax = stageHeight/30; zoomMax = (ZOOM_MAXDIST - ZOOM_MINDIST)/30; if (panelButton==ROTXD_PANELBUTTON) { xRot += xRotMax/10; if (xRot>xRotMax) xRot = xRotMax; movementType = ROTATE_MOVETYPE; } else if (panelButton==ROTXU_PANELBUTTON) { xRot -= xRotMax/10; if (xRot<-xRotMax) xRot = -xRotMax; movementType = ROTATE_MOVETYPE; } else if (panelButton==ROTYL_PANELBUTTON) { yRot += yRotMax/10; if (yRot>yRotMax) yRot = yRotMax; movementType = ROTATE_MOVETYPE; } else if (panelButton==ROTYR_PANELBUTTON) { yRot -= yRotMax/10; if (yRot<-yRotMax) yRot = -yRotMax; movementType = ROTATE_MOVETYPE; } else if (panelButton==ZOOMIN_PANELBUTTON) { zoom -= zoomMax/10; if (zoom<-zoomMax) zoom = -zoomMax; movementType = ZOOM_MOVETYPE; } else if (panelButton==ZOOMOUT_PANELBUTTON) { zoom += zoomMax/10; if (zoom>zoomMax) zoom = zoomMax; movementType = ZOOM_MOVETYPE; } else if (panelButton==PANXR_PANELBUTTON) { xPan += xPanMax/10; if (xPan>xPanMax) xPan = xPanMax; movementType = PAN_MOVETYPE; } else if (panelButton==PANXL_PANELBUTTON) { xPan -= xPanMax/10; if (xPan<-xPanMax) xPan = -xPanMax; movementType = PAN_MOVETYPE; } else if (panelButton==PANYU_PANELBUTTON) { yPan -= yPanMax/10; if (yPan<-yPanMax) yPan = -yPanMax; movementType = PAN_MOVETYPE; } else if (panelButton==PANYD_PANELBUTTON) { yPan += yPanMax/10; if (yPan>yPanMax) yPan = yPanMax; movementType = PAN_MOVETYPE; } else if (panelButton==ZOOM_PANELBUTTON) { if (zoom_slider){ zoom = zoom_slider_path._zoom - zoom_slider_path.current_z_distance; movementType = ZOOM_MOVETYPE; } } buttonPressed = false; } else if (movementState == PANELDECAY_STATE) { zoom /= velocityDecayRate; xRot /= velocityDecayRate; yRot /= velocityDecayRate; xPan /= velocityDecayRate; yPan /= velocityDecayRate; if (Math.abs(xRot)<0.001 && Math.abs(yRot) < 0.001 && Math.abs(zoom)<0.001 && Math.abs(xPan)<0.001 && Math.abs(yPan)<0.001) { movementState = STOP_STATE; } } else if (movementState==DRAG_STATE) { mousex = _root._xmouse; mousey = _root._ymouse; velx = 0.6*velx+0.4*(mousex-omousex); vely = 0.6*vely+0.4*(mousey-omousey); //To stop moving with zero speed if (Math.abs(velx) < 0.1 && Math.abs(vely) < 0.1) { movementState = STOP_STATE; } if (zoom_slider){ zoom_slider_path.bZoomChanged = true; } omousex = mousex; omousey = mousey; } else if (movementState==DECAY_STATE) { velx /= velocityDecayRate; vely /= velocityDecayRate; if (Math.abs(velx) < 0.1 && Math.abs(vely) < 0.1) { movementState = STOP_STATE; } if (zoom_slider){ zoom_slider_path.bZoomChanged = true; } } if (movementState != STOP_STATE) { //Get camera position matrix cameraMatrix = new Matrix3d(cameraObj.matrix); inverseMatrix = cameraMatrix.inverse(); rotationPointInCameraFrame = inverseMatrix.multiply(rotatePoint); if (movementType == ROTATE_MOVETYPE && (movementState == PANELDRAG_STATE || movementState == PANELDECAY_STATE)) { // rotate the camera with the control panel cameraRotation = new Matrix3d(); //start from identity cameraRotation.rotate(xRot, -yRot, 0); cameraMatrix = cameraRotation.multiply(cameraMatrix); cameraObj.matrix = cameraMatrix; rotationPointRelToCamera = cameraMatrix.multiply(rotationPointInCameraFrame); cameraObj.matrix.setTranslation(cameraMatrix.m30-rotationPointRelToCamera.x, cameraMatrix.m31-rotationPointRelToCamera.y, cameraMatrix.m32-rotationPointRelToCamera.z); } else { fov = degToRad*cameraObj.fieldOfView; zDistFromCamera = rotationPointInCameraFrame.z; if (zDistFromCamera<0.1) zDistFromCamera = 0.1; fact = stageWidth/(2*Math.tan(fov/2)*zDistFromCamera); midx = (rotationPointInCameraFrame.x * fact) + xProjectionCentre; midy = (-rotationPointInCameraFrame.y * fact) + yProjectionCentre; //midx and midy are the coordinates of the centre of rotation, relative to //projection centre if (movementType == ROTATE_MOVETYPE) { coffx = (omousex-midx); coffy = (omousey-midy); mouserad = Math.sqrt(coffx*coffx+coffy*coffy); mousespeed = Math.sqrt(velx*velx+vely*vely); if (mousespeed < 0.001) mousespeed = 0.001; // find radial and circumferential components radx = coffx/(mouserad+1); rady = coffy/(mouserad+1); cirx = rady; ciry = -radx; circum = velx*cirx+vely*ciry; //circumferential component of velocity radial = velx*radx+vely*rady; //radial component of velocity if (radial<0) radial = -radial; radial *= 0.28; circum *= 0.28; mouseMoveInX = velx/mousespeed; mouseMoveInY = vely/mousespeed; zAngle = Math.acos(mouseMoveInY); if (mouseMoveInX<0) zAngle = (3.141592*2.0)-zAngle; zAngle*= radToDeg; // rotate the camera cameraRotation = new Matrix3d(); //start from identity cameraRotation.rotate(0, 0, zAngle); cameraRotation.rotate(radial, 0, 0); cameraRotation.rotate(0, 0, -zAngle); cameraRotation.rotate(0, 0, -circum); cameraMatrix = cameraRotation.multiply(cameraMatrix); cameraObj.matrix = cameraMatrix; rotationPointRelToCamera = cameraMatrix.multiply(rotationPointInCameraFrame); cameraObj.matrix.setTranslation(cameraMatrix.m30-rotationPointRelToCamera.x, cameraMatrix.m31-rotationPointRelToCamera.y, cameraMatrix.m32-rotationPointRelToCamera.z); } else if (movementType == ZOOM_MOVETYPE) //zoom { dx = 0.0; dy = 0.0; ZOOM_SPEED = 0.5*(ZOOM_MAXDIST - ZOOM_MINDIST)/stageHeight; if (movementState == PANELDRAG_STATE || movementState == PANELDECAY_STATE) dz = zoom; else dz = vely * ZOOM_SPEED; // when zooming in, we want the object to drift towards the centre of projection //it has //panned off-centre if (dz < 0.0) { dx = dz*(midx - xProjectionCentre)/1000.0; dy = -dz*(midy - yProjectionCentre)/1000.0; } xx = cameraMatrix.m00; xy = cameraMatrix.m01; xz = cameraMatrix.m02; yx = cameraMatrix.m10; yy = cameraMatrix.m11; yz = cameraMatrix.m12; zx = cameraMatrix.m20; zy = cameraMatrix.m21; zz = cameraMatrix.m22; adx = -dx*xx - dy*yx - dz*zx; ady = -dx*xy - dy*yy - dz*zy; adz = -dx*xz - dy*yz - dz*zz; //Suppose new object position matrix cameraMatrix = new Matrix3d(cameraMatrix); cameraMatrix.translate(adx, ady, adz); //find the new centre of rotation in the frame of the camera inverseMatrix = cameraMatrix.inverse(); rotationPointInCameraFrame = inverseMatrix.multiply(rotatePoint); newZDistFromCamera = rotationPointInCameraFrame.z; if (zoom_slider){ zoom_slider_path.current_z_distance = newZDistFromCamera; } if (newZDistFromCamera < ZOOM_MINDIST && dz < 0.0) { dx = (zDistFromCamera>ZOOM_MINDIST)?dx*(zDistFromCamera - ZOOM_MINDIST)/(zDistFromCamera-newZDistFromCamera):0.0; dy = (zDistFromCamera>ZOOM_MINDIST)?dy*(zDistFromCamera - ZOOM_MINDIST)/(zDistFromCamera-newZDistFromCamera):0.0; dz = (zDistFromCamera>ZOOM_MINDIST)?dz*(zDistFromCamera - ZOOM_MINDIST)/(zDistFromCamera-newZDistFromCamera):0.0; vely = 0.0; zoom = 0; } else if (newZDistFromCamera > ZOOM_MAXDIST && dz > 0.0) { //if dz>0.0 dx==dy==0.0; dz = (zDistFromCameraminXLimit)?dx*(midx-minXLimit)/(midx-newmidx):0.0; velx = 0.0; xPan = 0; } else if (newmidx > maxXLimit && dx > 0.0) { dx = (midx 0.0) { dy = (midy>minYLimit)?dy*(midy-minYLimit)/(midy-newmidy):0.0; vely = 0.0; yPan = 0; } else if (newmidy > maxYLimit && dy < 0.0) { dy = (midy