/** * Generated by Verge3D Puzzles v.3.1.1 * Mon Jun 08 2020 10:05:57 GMT+0200 (Mitteleuropäische Sommerzeit) * Prefer not editing this file as your changes may get overridden once Puzzles are saved. * Check out https://www.soft8soft.com/docs/manual/en/introduction/Using-JavaScript.html * for the information on how to add your own JavaScript to Verge3D apps. */ 'use strict'; (function() { // global variables/constants used by puzzles' functions var _pGlob = {}; _pGlob.objCache = {}; _pGlob.fadeAnnotations = true; _pGlob.objClickCallbacks = []; _pGlob.pickedObject = ''; _pGlob.objHoverCallbacks = []; _pGlob.hoveredObject = ''; _pGlob.objMovementInfos = {}; _pGlob.objDragOverCallbacks = []; _pGlob.objDragOverInfoByBlock = {} _pGlob.dragMoveOrigins = {}; _pGlob.dragScaleOrigins = {}; _pGlob.mediaElements = {}; _pGlob.loadedFiles = {}; _pGlob.loadedFile = ''; _pGlob.animMixerCallbacks = []; _pGlob.arHitPoint = new v3d.Vector3(0, 0, 0); _pGlob.states = []; _pGlob.percentage = 0; _pGlob.animateParamUpdate = null; _pGlob.openedFile = ''; _pGlob.xrSessionAcquired = false; _pGlob.xrSessionCallbacks = []; _pGlob.screenCoords = new v3d.Vector2(); _pGlob.gamepadIndex = 0; _pGlob.AXIS_X = new v3d.Vector3(1, 0, 0); _pGlob.AXIS_Y = new v3d.Vector3(0, 1, 0); _pGlob.AXIS_Z = new v3d.Vector3(0, 0, 1); _pGlob.MIN_DRAG_SCALE = 10e-4; _pGlob.SET_OBJ_ROT_EPS = 1e-8; _pGlob.vec2Tmp = new v3d.Vector2(); _pGlob.vec2Tmp2 = new v3d.Vector2(); _pGlob.vec3Tmp = new v3d.Vector3(); _pGlob.vec3Tmp2 = new v3d.Vector3(); _pGlob.vec3Tmp3 = new v3d.Vector3(); _pGlob.vec3Tmp4 = new v3d.Vector3(); _pGlob.eulerTmp = new v3d.Euler(); _pGlob.eulerTmp2 = new v3d.Euler(); _pGlob.quatTmp = new v3d.Quaternion(); _pGlob.quatTmp2 = new v3d.Quaternion(); _pGlob.mat4Tmp = new v3d.Matrix4(); _pGlob.planeTmp = new v3d.Plane(); _pGlob.raycasterTmp = new v3d.Raycaster(); _pGlob.intervals = {}; _pGlob.wooProductInfo = {}; var _pPhysics = {}; _pPhysics.tickCallbacks = []; _pPhysics.syncList = []; // internal info _pPhysics.collisionData = []; // goes to collision callback _pPhysics.collisionInfo = { objectA: '', objectB: '', distance: 0, positionOnA: [0, 0, 0], positionOnB: [0, 0, 0], normalOnB: [0, 0, 0] }; var PL = v3d.PL = v3d.PL || {}; // a more readable alias for PL (stands for "Puzzle Logic") v3d.puzzles = PL; PL.procedures = PL.procedures || {}; PL.execInitPuzzles = function(options) { // always null, should not be available in "init" puzzles var appInstance = null; var _initGlob = {}; _initGlob.percentage = 0; _initGlob.output = { initOptions: { fadeAnnotations: true, useBkgTransp: false, preserveDrawBuf: false, useCompAssets: false, useFullscreen: true, useCustomPreloader: false, preloaderStartCb: function() {}, preloaderProgressCb: function() {}, preloaderEndCb: function() {}, } } // provide the container's id to puzzles that need access to the container _initGlob.container = options !== undefined && 'container' in options ? options.container : ""; return _initGlob.output; } PL.init = function(appInstance, initOptions) { initOptions = initOptions || {}; if ('fadeAnnotations' in initOptions) { _pGlob.fadeAnnotations = initOptions.fadeAnnotations; } this.procedures["Winkel"] = Winkel; var refr1_refr2_mirror, Winkel_Alpha_90, n_1, Wasser, Winkel_Alpha, n_2, Winkel_Beta, Winkel_Alpha_Round, Winkel_Beta_Round; // utility function envoked by almost all V3D-specific puzzles // filter off some non-mesh types function notIgnoredObj(obj) { return (obj.type !== "AmbientLight" && obj.name !== "" && !(obj.isMesh && obj.isMaterialGeneratedMesh)); } // utility function envoked by almost all V3D-specific puzzles // find first occurence of the object by its name function getObjectByName(objName) { var objFound; var runTime = _pGlob !== undefined; objFound = runTime ? _pGlob.objCache[objName] : null; if (objFound && objFound.name === objName) return objFound; appInstance.scene.traverse(function(obj) { if (!objFound && notIgnoredObj(obj) && (obj.name == objName)) { objFound = obj; if (runTime) { _pGlob.objCache[objName] = objFound; } } }); return objFound; } // utility function envoked by almost all V3D-specific puzzles // retrieve all objects on the scene function getAllObjectNames() { var objNameList = []; appInstance.scene.traverse(function(obj) { if (notIgnoredObj(obj)) objNameList.push(obj.name) }); return objNameList; } // utility function envoked by almost all V3D-specific puzzles // retrieve all objects which belong to the group function getObjectNamesByGroupName(targetGroupName) { var objNameList = []; appInstance.scene.traverse(function(obj){ if (notIgnoredObj(obj)) { var groupNames = obj.groupNames; if (!groupNames) return; for (var i = 0; i < groupNames.length; i++) { var groupName = groupNames[i]; if (groupName == targetGroupName) { objNameList.push(obj.name); } } } }); return objNameList; } // utility function envoked by almost all V3D-specific puzzles // process object input, which can be either single obj or array of objects, or a group function retrieveObjectNames(objNames) { var acc = []; retrieveObjectNamesAcc(objNames, acc); return acc; } function retrieveObjectNamesAcc(currObjNames, acc) { if (typeof currObjNames == "string") { acc.push(currObjNames); } else if (Array.isArray(currObjNames) && currObjNames[0] == "GROUP") { var newObj = getObjectNamesByGroupName(currObjNames[1]); for (var i = 0; i < newObj.length; i++) acc.push(newObj[i]); } else if (Array.isArray(currObjNames) && currObjNames[0] == "ALL_OBJECTS") { var newObj = getAllObjectNames(); for (var i = 0; i < newObj.length; i++) acc.push(newObj[i]); } else if (Array.isArray(currObjNames)) { for (var i = 0; i < currObjNames.length; i++) retrieveObjectNamesAcc(currObjNames[i], acc); } } /** * Retrieve coordinate system from the loaded scene */ function getCoordSystem() { var scene = appInstance.scene; if (scene && "v3d" in scene.userData && "coordSystem" in scene.userData.v3d) { return scene.userData.v3d.coordSystem; } else { // COMPAT: <2.17, consider replacing to 'Y_UP_RIGHT' for scenes with unknown origin return 'Z_UP_RIGHT'; } } /** * Transform coordinates from one space to another * Can be used with Vector3 or Euler. */ function coordsTransform(coords, from, to, noSignChange) { if (from == to) return coords; var y = coords.y, z = coords.z; if (from == 'Z_UP_RIGHT' && to == 'Y_UP_RIGHT') { coords.y = z; coords.z = noSignChange ? y : -y; } else if (from == 'Y_UP_RIGHT' && to == 'Z_UP_RIGHT') { coords.y = noSignChange ? z : -z; coords.z = y; } else { console.error('coordsTransform: Unsupported coordinate space'); } return coords; } /** * Verge3D euler rotation to Blender/Max shortest. * 1) Convert from intrinsic rotation (v3d) to extrinsic XYZ (Blender/Max default * order) via reversion: XYZ -> ZYX * 2) swizzle ZYX->YZX * 3) choose the shortest rotation to resemble Blender's behavior */ var eulerV3DToBlenderShortest = function() { var eulerTmp = new v3d.Euler(); var eulerTmp2 = new v3d.Euler(); var vec3Tmp = new v3d.Vector3(); return function(euler, dest) { var eulerBlender = eulerTmp.copy(euler).reorder('YZX'); var eulerBlenderAlt = eulerTmp2.copy(eulerBlender).makeAlternative(); var len = eulerBlender.toVector3(vec3Tmp).lengthSq(); var lenAlt = eulerBlenderAlt.toVector3(vec3Tmp).lengthSq(); dest.copy(len < lenAlt ? eulerBlender : eulerBlenderAlt); return coordsTransform(dest, 'Y_UP_RIGHT', 'Z_UP_RIGHT'); } }(); function RotationInterface() { /** * For user manipulations use XYZ extrinsic rotations (which * are the same as ZYX intrinsic rotations) * - Blender/Max/Maya use extrinsic rotations in the UI * - XYZ is the default option, but could be set from * some order hint if exported */ this._userRotation = new v3d.Euler(0, 0, 0, 'ZYX'); this._actualRotation = new v3d.Euler(); } Object.assign(RotationInterface, { initObject: function(obj) { if (obj.userData.v3d.puzzles === undefined) { obj.userData.v3d.puzzles = {} } if (obj.userData.v3d.puzzles.rotationInterface === undefined) { obj.userData.v3d.puzzles.rotationInterface = new RotationInterface(); } var rotUI = obj.userData.v3d.puzzles.rotationInterface; rotUI.updateFromObject(obj); return rotUI; } }); Object.assign(RotationInterface.prototype, { updateFromObject: function(obj) { var SYNC_ROT_EPS = 1e-8; if (!this._actualRotation.equalsEps(obj.rotation, SYNC_ROT_EPS)) { this._actualRotation.copy(obj.rotation); this._updateUserRotFromActualRot(); } }, getActualRotation: function(euler) { return euler.copy(this._actualRotation); }, setUserRotation: function(euler) { // don't copy the order, since it's fixed to ZYX for now this._userRotation.set(euler.x, euler.y, euler.z); this._updateActualRotFromUserRot(); }, getUserRotation: function(euler) { return euler.copy(this._userRotation); }, _updateUserRotFromActualRot: function() { var order = this._userRotation.order; this._userRotation.copy(this._actualRotation).reorder(order); }, _updateActualRotFromUserRot: function() { var order = this._actualRotation.order; this._actualRotation.copy(this._userRotation).reorder(order); } }); // getObjTransform puzzle function getObjTransform(objName, mode, coord) { if (!objName) return; var obj = getObjectByName(objName); if (!obj) return; var coordSystem = getCoordSystem(); var transformVal = 0; if (mode === 'rotation' && coordSystem == 'Z_UP_RIGHT') { transformVal = eulerV3DToBlenderShortest(obj.rotation, _pGlob.eulerTmp)[coord]; } else if (mode === 'rotation' && coordSystem == 'Y_UP_RIGHT') { // Maya coordinates // Use separate rotation interface to fix ambiguous rotations for Maya, // might as well do the same for Blender/Max. var rotUI = RotationInterface.initObject(obj); transformVal = rotUI.getUserRotation(_pGlob.eulerTmp)[coord]; } else { transformVal = coordsTransform(obj[mode].clone(), 'Y_UP_RIGHT', coordSystem, mode === 'scale')[coord]; } if (mode === 'rotation') { transformVal = v3d.MathUtils.radToDeg(transformVal); } return transformVal; } // toFixedPoint puzzle function toFixedPoint(num, prec) { prec = Math.pow(10, prec); return Math.round(num * prec)/prec; } function matGetValues(matName) { var mat = v3d.SceneUtils.getMaterialByName(appInstance, matName); if (!mat) return []; if (mat.isMeshNodeMaterial) return Object.keys(mat.nodeValueMap); else if (mat.isMeshStandardMaterial) return ['metalness', 'roughness', 'bumpScale', 'emissiveIntensity', 'envMapIntensity']; else return []; } // setMaterialValue puzzle function setMaterialValue(matName, valName, value) { var colors = matGetValues(matName); if (colors.indexOf(valName) < 0) return; var mats = v3d.SceneUtils.getMaterialsByName(appInstance, matName); for (var i = 0; i < mats.length; i++) { var mat = mats[i]; if (mat.isMeshNodeMaterial) { var valIdx = mat.nodeValueMap[valName]; mat.nodeValue[valIdx] = value; } else mat[valName] = value; if (mat === appInstance.worldMaterial) appInstance.updateEnvironment(mat); } } // applyObjLocalTransform puzzle function applyObjLocalTransform(objNames, mode, x, y, z) { objNames = retrieveObjectNames(objNames); if (!objNames) return; var defValue = mode == "scale" ? 1 : 0; if (typeof x != "number") x = defValue; if (typeof y != "number") y = defValue; if (typeof z != "number") z = defValue; var coords = coordsTransform(_pGlob.vec3Tmp.set(x, y, z), getCoordSystem(), 'Y_UP_RIGHT', mode == 'scale'); for (var i = 0; i < objNames.length; i++) { var objName = objNames[i]; if (!objName) continue; var obj = getObjectByName(objName); if (!obj) continue; // don't transform values for cameras, their local space happens // to be the same as for Blender/Max cameras, bcz their different // rest orientation balances difference in coordinate systems var useTransformed = !obj.isCamera; var xVal = useTransformed ? coords.x : x; var yVal = useTransformed ? coords.y : y; var zVal = useTransformed ? coords.z : z; switch (mode) { case "position": if (_pGlob.xrSessionAcquired && obj.isCamera) { v3d.WebXRUtils.translateVRCamera(obj, _pGlob.AXIS_X, xVal); v3d.WebXRUtils.translateVRCamera(obj, _pGlob.AXIS_Y, yVal); v3d.WebXRUtils.translateVRCamera(obj, _pGlob.AXIS_Z, zVal); } else { obj.translateX(xVal); obj.translateY(yVal); obj.translateZ(zVal); } break; case "rotation": if (_pGlob.xrSessionAcquired && obj.isCamera) { v3d.WebXRUtils.rotateVRCamera(obj, _pGlob.AXIS_X, v3d.Math.degToRad(xVal)); v3d.WebXRUtils.rotateVRCamera(obj, _pGlob.AXIS_Y, v3d.Math.degToRad(yVal)); v3d.WebXRUtils.rotateVRCamera(obj, _pGlob.AXIS_Z, v3d.Math.degToRad(zVal)); } else { obj.rotateX(v3d.Math.degToRad(xVal)); obj.rotateY(v3d.Math.degToRad(yVal)); obj.rotateZ(v3d.Math.degToRad(zVal)); } break; case "scale": obj.scale.x *= xVal; obj.scale.y *= yVal; obj.scale.z *= zVal; break; } obj.updateMatrixWorld(true); } } // show and hide puzzles function changeVis(objNames, bool) { objNames = retrieveObjectNames(objNames); if (!objNames) return; for (var i = 0; i < objNames.length; i++) { var objName = objNames[i] if (!objName) continue; var obj = getObjectByName(objName); if (!obj) continue; obj.visible = bool; } } // utility function used by the whenClicked, whenHovered and whenDraggedOver puzzles function initObjectPicking(callback, eventType, mouseDownUseTouchStart) { var elem = appInstance.renderer.domElement; elem.addEventListener(eventType, pickListener); if (eventType == "mousedown") { var touchEventName = mouseDownUseTouchStart ? "touchstart" : "touchend"; elem.addEventListener(touchEventName, pickListener); } var raycaster = new v3d.Raycaster(); function pickListener(event) { event.preventDefault(); var xNorm = 0, yNorm = 0; if (event instanceof MouseEvent) { xNorm = event.offsetX / elem.clientWidth; yNorm = event.offsetY / elem.clientHeight; } else if (event instanceof TouchEvent) { var rect = elem.getBoundingClientRect(); xNorm = (event.changedTouches[0].clientX - rect.left) / rect.width; yNorm = (event.changedTouches[0].clientY - rect.top) / rect.height; } _pGlob.screenCoords.x = xNorm * 2 - 1; _pGlob.screenCoords.y = -yNorm * 2 + 1; raycaster.setFromCamera(_pGlob.screenCoords, appInstance.camera); var objList = []; appInstance.scene.traverse(function(obj){objList.push(obj);}); var intersects = raycaster.intersectObjects(objList); if (intersects.length > 0) { var obj = intersects[0].object; callback(obj, event); } else { callback(null, event); } } } // utility function used by the whenDraggedOver puzzles function fireObjectPickingCallbacks(objName, source, index, cbParam) { for (var i = 0; i < source.length; i++) { var cb = source[i]; if (objectsIncludeObj([cb[0]], objName)) { cb[index](cbParam); } } } function objectsIncludeObj(objNames, testedObjName) { if (!testedObjName) return false; for (var i = 0; i < objNames.length; i++) { if (testedObjName == objNames[i]) { return true; } else { // also check children which are auto-generated for multi-material objects var obj = getObjectByName(objNames[i]); if (obj && obj.type == "Group") { for (var j = 0; j < obj.children.length; j++) { if (testedObjName == obj.children[j].name) { return true; } } } } } return false; } // utility function used by the whenClicked, whenHovered and whenDraggedOver puzzles function getPickedObjectName(obj) { // auto-generated from a multi-material object, use parent name instead if (obj.isMesh && obj.isMaterialGeneratedMesh && obj.parent) { return obj.parent.name; } else { return obj.name; } } // whenClicked puzzle initObjectPicking(function(obj) { // save the object for the pickedObject block _pGlob.pickedObject = obj ? getPickedObjectName(obj) : ''; _pGlob.objClickCallbacks.forEach(function(el) { var isPicked = obj && objectsIncludeObj(el.objNames, getPickedObjectName(obj)); el.callbacks[isPicked ? 0 : 1](); }); }, 'mousedown'); // whenClicked puzzle function registerOnClick(objNames, cbDo, cbIfMissedDo) { objNames = retrieveObjectNames(objNames) || []; var objNamesFiltered = objNames.filter(function(name) { return name; }); _pGlob.objClickCallbacks.push({ objNames: objNamesFiltered, callbacks: [cbDo, cbIfMissedDo] }); } // updateTextObject puzzle function updateTextObj(objNames, text) { objNames = retrieveObjectNames(objNames); if (!objNames) return; for (var i = 0; i < objNames.length; i++) { var objName = objNames[i]; if (!objName) continue; var obj = getObjectByName(objName); if (!obj || !obj.geometry || !obj.geometry.cloneWithText) continue; obj.geometry = obj.geometry.cloneWithText(String(text)); } } // setObjTransform puzzle function setObjTransform(objNames, mode, x, y, z, offset) { objNames = retrieveObjectNames(objNames); if (!objNames) return; function setObjProp(obj, prop, val) { if (!offset) { obj[mode][prop] = val; } else { if (mode != "scale") obj[mode][prop] += val; else obj[mode][prop] *= val; } } var inputsUsed = _pGlob.vec3Tmp.set(Number(x !== ''), Number(y !== ''), Number(z !== '')); var coords = _pGlob.vec3Tmp2.set(x || 0, y || 0, z || 0); if (mode === 'rotation') { // rotations are specified in degrees coords.multiplyScalar(v3d.Math.DEG2RAD); } var coordSystem = getCoordSystem(); coordsTransform(inputsUsed, coordSystem, 'Y_UP_RIGHT', true); coordsTransform(coords, coordSystem, 'Y_UP_RIGHT', mode === 'scale'); for (var i = 0; i < objNames.length; i++) { var objName = objNames[i]; if (!objName) continue; var obj = getObjectByName(objName); if (!obj) continue; if (mode === 'rotation' && coordSystem == 'Z_UP_RIGHT') { // Blender/Max coordinates // need all the rotations for order conversions, especially if some // inputs are not specified var euler = eulerV3DToBlenderShortest(obj.rotation, _pGlob.eulerTmp); coordsTransform(euler, coordSystem, 'Y_UP_RIGHT'); if (inputsUsed.x) euler.x = offset ? euler.x + coords.x : coords.x; if (inputsUsed.y) euler.y = offset ? euler.y + coords.y : coords.y; if (inputsUsed.z) euler.z = offset ? euler.z + coords.z : coords.z; /** * convert from Blender/Max default XYZ extrinsic order to v3d XYZ * intrinsic with reversion (XYZ -> ZYX) and axes swizzling (ZYX -> YZX) */ euler.order = "YZX"; euler.reorder(obj.rotation.order); obj.rotation.copy(euler); } else if (mode === 'rotation' && coordSystem == 'Y_UP_RIGHT') { // Maya coordinates // Use separate rotation interface to fix ambiguous rotations for Maya, // might as well do the same for Blender/Max. var rotUI = RotationInterface.initObject(obj); var euler = rotUI.getUserRotation(_pGlob.eulerTmp); // TODO(ivan): this probably needs some reasonable wrapping if (inputsUsed.x) euler.x = offset ? euler.x + coords.x : coords.x; if (inputsUsed.y) euler.y = offset ? euler.y + coords.y : coords.y; if (inputsUsed.z) euler.z = offset ? euler.z + coords.z : coords.z; rotUI.setUserRotation(euler); rotUI.getActualRotation(obj.rotation); } else { if (inputsUsed.x) setObjProp(obj, "x", coords.x); if (inputsUsed.y) setObjProp(obj, "y", coords.y); if (inputsUsed.z) setObjProp(obj, "z", coords.z); } obj.updateMatrixWorld(true); } } // outline puzzle function outline(objNames, doWhat) { objNames = retrieveObjectNames(objNames); if (!objNames) return; if (!appInstance.postprocessing || !appInstance.postprocessing.outlinePass) return; var outlineArray = appInstance.postprocessing.outlinePass.selectedObjects; for (var i = 0; i < objNames.length; i++) { var objName = objNames[i]; var obj = getObjectByName(objName); if (!obj) continue; if (doWhat == "ENABLE") { if (outlineArray.indexOf(obj) == -1) outlineArray.push(obj); } else { var index = outlineArray.indexOf(obj); if (index > -1) outlineArray.splice(index, 1); } } } // whenHovered puzzle initObjectPicking(function(obj) { var prevHovered = _pGlob.hoveredObject; var currHovered = obj ? getPickedObjectName(obj) : ""; if (prevHovered == currHovered) return; // first - all "out" callbacks, then - all "over" _pGlob.objHoverCallbacks.forEach(function(el) { if (objectsIncludeObj(el.objNames, prevHovered)) { // ensure the correct value of the hoveredObject block _pGlob.hoveredObject = prevHovered; el.callbacks[1](); } }); _pGlob.objHoverCallbacks.forEach(function(el) { if (objectsIncludeObj(el.objNames, currHovered)) { // ensure the correct value of the hoveredObject block _pGlob.hoveredObject = currHovered; el.callbacks[0](); } }); _pGlob.hoveredObject = currHovered; }, 'mousemove'); // whenHovered puzzle function registerOnHover(objNames, callback_over, callback_out) { objNames = retrieveObjectNames(objNames) || []; var objNamesFiltered = objNames.filter(function(name) { return name; }); _pGlob.objHoverCallbacks.push({ objNames: objNamesFiltered, callbacks: [callback_over, callback_out] }); } // dragRotate puzzle function dragRotate(objNames, mode, isParentSpace, blockId, parentDragOverBlockId) { if (!appInstance.camera) return; objNames = retrieveObjectNames(objNames); if (!objNames) return; var info = _pGlob.objDragOverInfoByBlock[parentDragOverBlockId]; if (!info) return; var coordSystem = getCoordSystem(); for (var i = 0; i < objNames.length; i++) { var obj = getObjectByName(objNames[i]); if (!obj) { continue; } if (mode == "X" || mode == "Y" || mode == "Z") { var objPos = obj.getWorldPosition(_pGlob.vec3Tmp); objPos.project(appInstance.camera); var objX = (objPos.x + 1) / 2 * appInstance.getWidth(); var objY = (-objPos.y + 1) / 2 * appInstance.getHeight(); var vecFrom = _pGlob.vec2Tmp.set(info.prevX - objX, objY - info.prevY); var vecTo = _pGlob.vec2Tmp2.set(info.currX - objX, objY - info.currY); if (coordSystem == 'Z_UP_RIGHT') var axis = _pGlob.vec3Tmp.copy(mode == "X" ? _pGlob.AXIS_X : (mode == "Y" ? _pGlob.AXIS_Z : _pGlob.AXIS_Y)); else var axis = _pGlob.vec3Tmp.copy(mode == "X" ? _pGlob.AXIS_X : (mode == "Y" ? _pGlob.AXIS_Y : _pGlob.AXIS_Z)); var quat = _pGlob.quatTmp.setFromAxisAngle(axis, vecTo.angle() - vecFrom.angle()); // a rotation axis pointing backwards (i.e. co-directionally // aligned with the view vector) should have inverted rotation var objToCalcSpace = isParentSpace && obj.parent ? obj.parent : obj; axis.applyQuaternion(objToCalcSpace.getWorldQuaternion(_pGlob.quatTmp2)); var viewVec = appInstance.camera.getWorldDirection(_pGlob.vec3Tmp2); if (viewVec.dot(axis) > 0) { quat.conjugate(); } if (isParentSpace) { obj.quaternion.premultiply(quat); } else { obj.quaternion.multiply(quat); } obj.updateMatrixWorld(true); } } } function eventGetOffsetCoords(e, touchId, dest) { if (e instanceof MouseEvent) { dest.set(e.offsetX, e.offsetY); } else if (window.TouchEvent && e instanceof TouchEvent) { var rect = e.target.getBoundingClientRect(); var touches = e.touches; if (e.type == "touchstart" || e.type == "touchend" || e.type == "touchmove") { touches = e.changedTouches; } var touch = touches[0]; for (var i = 0; i < touches.length; i++) { if (touches[i].identifier == touchId) { touch = touches[i]; break; } } dest.set(touch.clientX - rect.left, touch.clientY - rect.top); } return dest; } function eventTouchIdGetFirst(e) { if (e instanceof MouseEvent) { return -1; } else if (window.TouchEvent && e instanceof TouchEvent) { if (e.type == "touchstart" || e.type == "touchend" || e.type == "touchmove") { return e.changedTouches[0].identifier; } else { return e.touches[0].identifier; } } return -1; } /** * For "touchstart", "touchend" and "touchmove" events returns true if a touch * object with the provided touch id is in the changedTouches array, otherwise * - false. For other events returns true. */ function eventTouchIdChangedFilter(e, touchId) { if (window.TouchEvent && e instanceof TouchEvent) { if (e.type == "touchstart" || e.type == "touchend" || e.type == "touchmove") { var isChanged = false; for (var i = 0; i < e.changedTouches.length; i++) { if (e.changedTouches[i].identifier == touchId) { isChanged = true; break; } } return isChanged; } } return true; } function initDragOverInfo() { return { draggedObjName: '', downX: 0, downY: 0, prevX: 0, prevY: 0, currX: 0, currY: 0, isDowned: false, isMoved: false, touchId: -1 }; } // whenDraggedOver puzzle initObjectPicking(function(obj, downEvent) { if (!obj) { return; } var objName = getPickedObjectName(obj); fireObjectPickingCallbacks(objName, _pGlob.objDragOverCallbacks, 1, { downEvent: downEvent, draggedObjName: objName }); }, "mousedown", true); // whenDraggedOver puzzle function registerOnDrag(objNames, callback_start, callback_move, callback_drop, blockId) { objNames = retrieveObjectNames(objNames); if (!objNames) return; var cb = function(cbParam) { if (appInstance.controls) { appInstance.controls.enabled = false; } if (!(blockId in _pGlob.objDragOverInfoByBlock)) { _pGlob.objDragOverInfoByBlock[blockId] = initDragOverInfo(); } var info = _pGlob.objDragOverInfoByBlock[blockId]; // NOTE: don't use more than one pointing event, e.g. don't process // some events related to multitouch actions if (info.isDowned) { return; } var touchId = eventTouchIdGetFirst(cbParam.downEvent); var coords = eventGetOffsetCoords(cbParam.downEvent, touchId, _pGlob.vec2Tmp); info.downX = info.prevX = info.currX = coords.x; info.downY = info.prevY = info.currY = coords.y; info.touchId = touchId; info.isDowned = true; info.isMoved = false; info.draggedObjName = cbParam.draggedObjName; callback_start(); var elem = appInstance.container; var moveCb = function(e) { if (!eventTouchIdChangedFilter(e, info.touchId)) { // don't handle events not intended for this particular touch return; } var coords = eventGetOffsetCoords(e, info.touchId, _pGlob.vec2Tmp); info.prevX = info.currX; info.prevY = info.currY; info.currX = coords.x; info.currY = coords.y; callback_move(); info.isMoved = true; } var upCb = function(e) { if (!eventTouchIdChangedFilter(e, info.touchId)) { // don't handle events not intended for this particular touch return; } var coords = eventGetOffsetCoords(e, info.touchId, _pGlob.vec2Tmp); info.currX = coords.x; info.currY = coords.y; info.prevX = info.currX; info.prevY = info.currY; callback_drop(); info.isDowned = false; elem.removeEventListener("mousemove", moveCb, false); elem.removeEventListener("touchmove", moveCb, false); elem.removeEventListener("mouseup", upCb, false); elem.removeEventListener("touchend", upCb, false); if (appInstance.controls) { appInstance.controls.enabled = true; } } elem.addEventListener("mousemove", moveCb, false); elem.addEventListener("touchmove", moveCb, false); elem.addEventListener("mouseup", upCb, false); elem.addEventListener("touchend", upCb, false); } for (var i = 0; i < objNames.length; i++) { var objName = objNames[i]; if (!objName) continue; _pGlob.objDragOverCallbacks.push([objName, cb]); } } // Describe this function... function Winkel() { if (refr1_refr2_mirror == 0) { Winkel_Beta = Math.asin((n_1 * Math.sin(Winkel_Alpha / 180 * Math.PI)) / n_2) / Math.PI * 180; setObjTransform('3 gebrochener Lichtstrahl', 'rotation', '', '', Winkel_Beta, false); updateTextObj('Text_Winkel3', Winkel_Beta_Round); setMaterialValue('EmmisionLaser_2', 'Value', (Winkel_Alpha / (90)) * 0.95); setMaterialValue('EmmisionLaser_3', 'Value', (Winkel_Alpha / (90)) * 0.95); } else if (refr1_refr2_mirror == 1) { if ((n_2 * Math.sin(Winkel_Alpha / 180 * Math.PI)) / n_1 < 1) { Winkel_Beta = Math.asin((n_2 * Math.sin(Winkel_Alpha / 180 * Math.PI)) / n_1) / Math.PI * 180; setMaterialValue('EmmisionLaser_2', 'Value', (n_2 * Math.sin(Winkel_Alpha / 180 * Math.PI)) / n_1); setMaterialValue('EmmisionLaser_3', 'Value', (n_2 * Math.sin(Winkel_Alpha / 180 * Math.PI)) / n_1); changeVis('3 gebrochener Lichtstrahl', true); changeVis('Text3_gebr', true); changeVis('Text_Beta', true); changeVis('Winkel_Beta', true); updateTextObj('Text_Winkel3', Winkel_Beta_Round); } else { changeVis('3 gebrochener Lichtstrahl', false); changeVis('Text3_gebr', false); changeVis('Text_Beta', false); changeVis('Winkel_Beta', false); updateTextObj('Text_Winkel3', '--'); setMaterialValue('EmmisionLaser_2', 'Value', 0.95); } setObjTransform('3 gebrochener Lichtstrahl', 'rotation', '', '', Winkel_Beta, false); } else if (refr1_refr2_mirror == 2) { setMaterialValue('EmmisionLaser_2', 'Value', 0.95); updateTextObj('Text_Winkel3', '--'); } updateTextObj('Text_Winkel1', Winkel_Alpha_Round); updateTextObj('Text_Winkel2', Winkel_Alpha_Round); } // bloom puzzle function bloom(threshold, strength, radius) { appInstance.enablePostprocessing([{ type: 'bloom', threshold: threshold, strength: strength, radius: radius }]); } // refraction puzzle function refraction(matName, distance, renderAfterNames) { var matNames = Array.isArray(matName) ? matName : [matName]; var mats = []; matNames.forEach(function(name) { mats = mats.concat(v3d.SceneUtils.getMaterialsByName(appInstance, name)); }); var objects = []; for (var i = 0; i < mats.length; i++) { var mat = mats[i]; appInstance.scene.traverse(function(obj) { if (obj.material && obj.material == mat) objects.push(obj); }); } // no need if (!objects.length) return; var renderAfter = []; renderAfterNames = retrieveObjectNames(renderAfterNames); if (!renderAfterNames) return; for (var i = 0; i < renderAfterNames.length; i++) { var obj = getObjectByName(renderAfterNames[i]); if (obj) renderAfter.push(obj); } appInstance.enablePostprocessing([{ type: 'ssr', useRefract: true, simpleRefraction: true, objects: objects, maxDistance: distance, renderAfter: renderAfter }]); } registerOnClick('01_Refr1_Refr2_Mirror', function() { Winkel_Alpha_90 = getObjTransform('1 einfallender Lichtstrahl', 'rotation', 'z') + (90); Winkel_Alpha = getObjTransform('1 einfallender Lichtstrahl', 'rotation', 'z') + (0); Winkel_Alpha_Round = toFixedPoint(Winkel_Alpha, 1); Winkel_Beta_Round = toFixedPoint(Winkel_Beta, 1); if (refr1_refr2_mirror == 0) { refr1_refr2_mirror = 1; Winkel(); setMaterialValue('Refr1_Refr2_Mirror', 'mirror', 1); applyObjLocalTransform('Halbzylinder', 'rotation', '', '', 180); } else if (refr1_refr2_mirror == 1) { refr1_refr2_mirror = 2; Winkel(); setMaterialValue('Refr1_Refr2_Mirror', 'mirror', 0); setMaterialValue('Refr1_Refr2_Mirror', 'refr', 0); changeVis('Halbzylinder', false); changeVis('HalbzylinderWire', false); changeVis('Text_Beta', false); changeVis('Winkel_Beta', false); changeVis('3 gebrochener Lichtstrahl', false); changeVis('Spiegel', true); } else if (refr1_refr2_mirror == 2) { refr1_refr2_mirror = 0; Winkel(); setMaterialValue('Refr1_Refr2_Mirror', 'refr', 1); changeVis('Spiegel', false); applyObjLocalTransform('Halbzylinder', 'rotation', '', '', 180); changeVis('Halbzylinder', true); changeVis('HalbzylinderWire', true); changeVis('3 gebrochener Lichtstrahl', true); changeVis('Text_Beta', true); changeVis('Winkel_Beta', true); } }, function() {}); refr1_refr2_mirror = 0; n_1 = 1; n_2 = 1.33; Wasser = 0; Winkel_Alpha_Round = (45); Winkel_Beta_Round = (32.2); changeVis('Spiegel', false); updateTextObj('Text_Winkel1', Winkel_Alpha_Round); updateTextObj('Text_Winkel2', Winkel_Alpha_Round); updateTextObj('Text_Winkel3', Winkel_Beta_Round); setObjTransform('3 gebrochener Lichtstrahl', 'rotation', '', '', 29, false); setMaterialValue('EmmisionLaser_2', 'Value', 0.4); setMaterialValue('EmmisionLaser_3', 'Value', 0.5); registerOnHover('1 einfallender Lichtstrahl', function() { outline('1 einfallender Lichtstrahl', 'ENABLE'); outline('Laser', 'ENABLE'); }, function() { outline('1 einfallender Lichtstrahl', 'ENABLE'); outline('Laser', 'DISABLE'); }); registerOnDrag('1 einfallender Lichtstrahl', function() {}, function() { Winkel_Alpha_90 = getObjTransform('1 einfallender Lichtstrahl', 'rotation', 'z') + (90); Winkel_Alpha = getObjTransform('1 einfallender Lichtstrahl', 'rotation', 'z') + (0); Winkel_Alpha_Round = toFixedPoint(Winkel_Alpha, 1); Winkel_Beta_Round = toFixedPoint(Winkel_Beta, 1); if (Winkel_Alpha_90 < (180.1) && Winkel_Alpha_90 > (89.9)) { dragRotate('1 einfallender Lichtstrahl', 'Z', true, '*D~_#.zm^Z@4l-9WpY5_', ',;~9?(Q+CU6K1i6.p|yj'); setObjTransform('2 reflektierter Lichtstrahl', 'rotation', '', '', -1 * getObjTransform('1 einfallender Lichtstrahl', 'rotation', 'z'), false); Winkel(); } else if (Winkel_Alpha_90 > (180.1)) { setObjTransform('1 einfallender Lichtstrahl', 'rotation', '', '', 90, false); } else if (Winkel_Alpha_90 < (89.9)) { setObjTransform('1 einfallender Lichtstrahl', 'rotation', '', '', 0, false); } else { } }, function() {}, ',;~9?(Q+CU6K1i6.p|yj'); bloom(0.8, 0.9, 0.7); refraction('Glas', 1, 'HalbzylinderWire'); } // end of PL.init function })(); // end of closure /* ================================ end of code ============================= */