var RTWebXRAPIVersion = "1.0.3"; const ROTATION_RATE = 3.0; const SCALE_SNAP_HIGH = 2.0; const SCALE_SNAP_LOW = 1 / SCALE_SNAP_HIGH; var xrImmersiveRefSpace = null; var xrViewerSpace, xrHitTestSource, xrHitTestSourceForTransient; var btnXR = null; var goalPosition = [0, 0, 0], placeInitially = true, modelplaced = true, gotoPos = null; var nInitialPlaceEstimates = 0, goalScale = 0.01; var inputSource, isTranslating = false, isRotating = false, isScaling = false, lastScalar = 0, goalYaw = 0, saveTranslatingOffset = null; var omousev = [0, 0, 1]; var initParamsXR = null; //var XRFrameCycling = false; var fingerLastPosition = null; function checkForXR(initParams) { //console.log("->checkForXR navigator.xr " + navigator.xr); //console.log(navigator.userAgent); //console.log("RTXR Version " + RTWebXRAPIVersion); if (navigator.xr) { navigator.xr.isSessionSupported('immersive-ar').then((supported) => { if (supported) { btnXR = document.getElementById("btnXR"); qrCode = document.getElementById("qrCode"); $("#btnXR").click(function() { onRequestSession() }); //btnXR.addEventListener("click", onRequestSession, false); qrCode.style.display = "none"; btnXR.style.display = "block"; var accordionContent = document.querySelectorAll('.accordionContent'); for (var i = 0; i <= accordionContent.length - 1; i++) { accordionContent[i].style.top = '160px'; } } }); if (typeof(initParams) != undefined) initParamsXR = initParams; } } function onRequestSession() { // scene.groupApplyState("AR_view_HDRI_exr");//Commented. We will use the original HDRI at the moment // console.log("AR_view_HDRI_exr state called"); //scene.groupApplyState("Mclaren_Studio_env_HDRi_07_WebXR_exr"); //console.log("->onReqSess"); var features = { requiredFeatures: ['local', 'hit-test'] }; var ov = document.getElementById("infinityrt-overlay"); var instructions = document.getElementById("infinityrt-instructions"); if (ov) { features['optionalFeatures'] = ['dom-overlay']; features['domOverlay'] = { root: ov }; } return navigator.xr.requestSession('immersive-ar', features).then((session) => { //console.log("--onReqSess " + session + " blendmode " + session.environmentBlendMode); if (instructions) { instructions.style.display = 'block'; instructions.style.opacity = 1; } else if (ov) { ov.style.display = 'block'; ov.style.opacity = 1; } setTimeout(function () { HideDOMOverlay(true); }, 5000); btnXR.session = session; if (onImmersiveStart) onImmersiveStart(); session.isImmersive = true; onSessionStarted(session); }); } function onSessionStarted(session) { //console.log("->onSessSt"); session.addEventListener('end', onSessionEnded); session.addEventListener("selectstart", onSelectStart); session.addEventListener("selectend", onSelectEnd); session.updateRenderState({ baseLayer: new XRWebGLLayer(session, scene.gl, { framebufferScaleFactor: (scene.arFBScale != undefined) ? scene.arFBScale : 0.5 }), depthNear: scene._zNearMin }); //console.log("--onSessSt updateRenderState complete"); if (session.isImmersive) { //console.log("--onSessSt requested refsp viewer"); session.requestReferenceSpace('viewer').then((refSpace) => { //console.log("--onSessSt recv viewer " + refSpace); xrViewerSpace = refSpace; session.requestHitTestSource({ space: xrViewerSpace }).then((hitTestSource) => { xrHitTestSource = hitTestSource; }); session.requestHitTestSourceForTransientInput({ profile: 'generic-touchscreen' }).then((hitTestSource) => { xrHitTestSourceForTransient = hitTestSource; }); }); //console.log("--onSessSt requested refsp local"); session.requestReferenceSpace(session.isImmersive ? 'local' : 'viewer').then((refSpace) => { //console.log("--onSessSt recv local " + refSpace); if (session.isImmersive) xrImmersiveRefSpace = refSpace; /*var id =*/ session.requestAnimationFrame(onXRFrame); //console.log("--onSessSt first requestAnimationFrame " + id); }); } } function HideDOMOverlay(withinAR) { var instructions = document.getElementById("infinityrt-instructions"); if (instructions) { instructions.style.opacity = 0; if (!withinAR) instructions.style.display = "none"; } else { var ov = document.getElementById("infinityrt-overlay"); ov.style.opacity = 0; if (!withinAR) ov.style.display = "none"; } } function onEndSession(session) { session.end(); } function onSessionEnded(event) { HideDOMOverlay(false); if (event.session.isImmersive) { btnXR.session = null; goalPosition = [0,0,0]; placeInitially = true; nInitialPlaceEstimates = 0; modelplaced = true; gotoPos = null; goalScale = 0.01; xrImmersiveRefSpace = null; xrViewerSpace = null; xrHitTestSource = null; xrHitTestSourceForTransient = null; isTranslating = false; isRotating = false; isScaling = false; lastScalar = 0; goalYaw = 0; if (onImmersiveEnd) onImmersiveEnd(); if (typeof(frameUpdate) != "undefined") window.requestAnimationFrame(frameUpdate); } } function ensureCameraIsOutsideObj(cammatrix, targetPos) { var campos = [cammatrix[12], cammatrix[13], cammatrix[14]]; var bbmin = infinityrt_vertex_mul_const(scene._bbmin, goalScale), bbmax = infinityrt_vertex_mul_const(scene._bbmax, goalScale); //console.log("campos " + campos); if (!(campos[0] >= (bbmin[0] + targetPos[0]) && campos[0] < (bbmax[0] + targetPos[0]) && campos[2] >= (bbmin[2] + targetPos[2]) && campos[2] < (bbmax[2] + targetPos[2]))) return targetPos; //console.log("inside need to move"); var camzv = infinityrt_normalize([cammatrix[8], 0, cammatrix[10]]); var bbsize = infinityrt_vertex_sub(bbmax, bbmin); var offsetlen = (Math.abs(bbsize[0]) > Math.abs(bbsize[2])) ? bbsize[0] : bbsize[2]; targetPos[0] -= offsetlen * camzv[0]; targetPos[2] -= offsetlen * camzv[2]; return targetPos; } function ensureValidLocation(phitpos) { if (phitpos[1] > 0 || phitpos[1] < -2) return false; return true; } //var firstdrawXR = true; //DBG//var posematrix = null; function onXRFrame(t, frame) { //XRFrameCycling = true; //var fdXR = false; //if (firstdrawXR) { // firstdrawXR = false; // fdXR = true; // console.log("--XRFrame"); //} var session = frame.session; //if (fdXR) { // console.log("--XRFrame session.isImmersive " + session.isImmersive) //} session.requestAnimationFrame(onXRFrame); if (session.isImmersive) { //if (fdXR) // console.log("--XRFrame processInput") //DBG//posematrix = null; //DBG//var pose = frame.getViewerPose(xrImmersiveRefSpace); //DBG//if (pose && pose.transform) { //DBG// var hitTestResults = frame.getHitTestResults(xrHitTestSource); //DBG// if (hitTestResults.length > 0) //DBG// posematrix = hitTestResults[0].getPose(xrImmersiveRefSpace).transform.matrix; //DBG//} processInput(frame); //if (fdXR) // console.log("--XRFrame getViewerPose") var pose = frame.getViewerPose(xrImmersiveRefSpace); if (pose && pose.transform) { var cammatrix = session.cammatrix = pose.transform.matrix.slice(); scene.xrprojmatrix = pose.views[0].projectionMatrix; if (gotoPos) { var animcomplete = false, s0 = (now() - gotoPos.st) / gotoPos.dur; if (s0 >= 1.0) { s0 = 1.0; animcomplete = true; } goalPosition = infinityrt_vertex_scladd(gotoPos.t0, gotoPos.td, s0); modelplaced = true; if (animcomplete) gotoPos = null; } else if (placeInitially) { var hitTestResults = frame.getHitTestResults(xrHitTestSource); if (hitTestResults.length > 0) { if (nInitialPlaceEstimates++ < 40) { // Require N estimates before using so ignore PlaceInFront(cammatrix); } else { //console.log("--XRFrame fromTransientHit, cammatrix pos " + cammatrix[12] + "," + cammatrix[13] + "," + cammatrix[14]); var phit = hitTestResults[0].getPose(xrImmersiveRefSpace); var phitpos = [phit.transform.position.x, phit.transform.position.y, phit.transform.position.z]; //console.log("Hit " + phit.transform.position.x + "," + phit.transform.position.y + "," + phit.transform.position.z); if (ensureValidLocation(phitpos)) { phitpos = ensureCameraIsOutsideObj(cammatrix, phitpos); //goalPosition = [phit.transform.position.x, phit.transform.position.y, phit.transform.position.z]; gotoPos = { t0: goalPosition, td: infinityrt_vertex_sub(phitpos, goalPosition), st: now(), dur: 1000 }; placeInitially = false; HideDOMOverlay(true); //console.log("fromTransientHit goto goalPosition " + phit.transform.position.x + "," + phit.transform.position.y + "," + phit.transform.position.z); } else { PlaceInFront(cammatrix); } } } else { PlaceInFront(cammatrix); } } /*if (placeInitially || getSceneHit(session, scene, 0, 0) != null) { var hitTestResults = frame.getHitTestResults(xrHitTestSource); if (hitTestResults.length > 0) { var phit = hitTestResults[0].getPose(xrImmersiveRefSpace); if (gotoPos) { gotoPos.td[1] = phit.transform.position.y - gotoPos.t0[1]; } else { goalPosition[1] = phit.transform.position.y; modelplaced = true; } } }*/ if (modelplaced) { modelplaced = false; goalPosition = ensureCameraIsOutsideObj(cammatrix, goalPosition); //console.log("ModelPlaced: goalScale " + goalScale + " goalPosition " + goalPosition[0] + "," + goalPosition[1] + "," + goalPosition[2]); for (var i = 0; i < scene.children.length; i++) { var sC = scene.children[i], matC = storedStateStd.rootMatrices[i]; sC.matrix[0] = matC[0] * goalScale; sC.matrix[1] = matC[1] * goalScale; sC.matrix[2] = matC[2] * goalScale; sC.matrix[4] = matC[4] * goalScale; sC.matrix[5] = matC[5] * goalScale; sC.matrix[6] = matC[6] * goalScale; sC.matrix[8] = matC[8] * goalScale; sC.matrix[9] = matC[9] * goalScale; sC.matrix[10] = matC[10] * goalScale; sC.matrix[12] = goalPosition[0]; sC.matrix[13] = goalPosition[1] - (scene._bbmin[1] * goalScale); sC.matrix[14] = goalPosition[2]; sC.matrixDirty = true; } scene.matrixDirty = true; scene._hierarchyDirty = true; } scene._zNearMin = storedStateStd._zNearMin * goalScale; scene.setViewMatrix(cammatrix); scene.setModelMatrix(scene._nav.NavCreateModelMatrix(scene._initialNavMatrix)); scene.drawXRFrame(frame, pose); scene.clearRefine(); } } } function PlaceInFront(cammatrix) { //console.log("--XRFrame placedInFront, cammatrix pos " + cammatrix[12] + "," + cammatrix[13] + "," + cammatrix[14]); var dir = [-cammatrix[8], -cammatrix[9], -cammatrix[10]]; var bbmin = infinityrt_vertex_mul_const(scene._bbmin, goalScale), bbmax = infinityrt_vertex_mul_const(scene._bbmax, goalScale); var bbsize = infinityrt_vertex_sub(bbmax, bbmin); var offsetlen = (Math.abs(bbsize[0]) > Math.abs(bbsize[2])) ? bbsize[0] : bbsize[2]; offsetlen = (Math.abs(bbsize[1]) > Math.abs(offsetlen)) ? bbsize[1] : offsetlen; dir = infinityrt_vertex_mul_const(dir, offsetlen * 2); goalPosition = infinityrt_vertex_add([cammatrix[12], cammatrix[13], cammatrix[14]], dir); modelplaced = true; //console.log("pIF " + goalPosition[0] + "," + goalPosition[1] + "," + goalPosition[2]); } function getCameraCreateRay(session, s, factorX, factorY) { var cammatrix = session.cammatrix; var ytangle = 1 / s.xrprojmatrix[5]; var w = (s.drawFBO != null) ? s.drawFBO._nFBOWidth : s.viewwidth; var h = (s.drawFBO != null) ? s.drawFBO._nFBOHeight : s.viewheight; var xtangle = ytangle * (w / h); var xv = infinityrt_vertex_mul_const([cammatrix[0], cammatrix[1], cammatrix[2]], xtangle), yv = infinityrt_vertex_mul_const([cammatrix[4], cammatrix[5], cammatrix[6]], ytangle); var dir = [-cammatrix[8], -cammatrix[9], -cammatrix[10]]; dir = infinityrt_vertex_scladd(dir, xv, factorX); dir = infinityrt_vertex_scladd(dir, yv, factorY); return infinityrt_normalize(dir); } function getSceneHit(session, s, factorX, factorY) { var cammatrix = session.cammatrix; if (!cammatrix) return null; var org = [cammatrix[12], cammatrix[13], cammatrix[14]]; var dir = getCameraCreateRay(session, s, factorX, factorY); // invert scene transformation to maintain AABB var matWithOffset = s.children[0].matrix; var matinv = MatrixInvert(matWithOffset); org = PointTransform([org[0], org[1], org[2], 1], matinv); dir = PointTransform([dir[0], dir[1], dir[2], 0], matinv); //console.log("org " + org[0] + "," + org[1] + "," + org[2] + " dir " + dir[0] + "," + dir[1] + "," + dir[2]); var bbmin = s._bbmin; var bbmax = s._bbmax; //console.log("bbmin " + bbmin[0] + "," + bbmin[1] + "," + bbmin[2] + " bbmax " + bbmax[0] + "," + bbmax[1] + "," + bbmax[2]); var dirfrac = [1.0 / dir[0], 1.0 / dir[1], 1.0 / dir[2]]; var t1 = (bbmin[0] - org[0]) * dirfrac[0]; var t2 = (bbmax[0] - org[0]) * dirfrac[0]; var t3 = (bbmin[1] - org[1]) * dirfrac[1]; var t4 = (bbmax[1] - org[1]) * dirfrac[1]; var t5 = (bbmin[2] - org[2]) * dirfrac[2]; var t6 = (bbmax[2] - org[2]) * dirfrac[2]; var tmin = Math.min(t1, t2); var tminy = Math.min(t3, t4); if (tminy > tmin) tmin = tminy; var tminz = Math.min(t5, t6); if (tminz > tmin) tmin = tminz; var tmax = Math.min(Math.min(Math.max(t1, t2), Math.max(t3, t4)), Math.max(t5, t6)); if (tmax < 0) return null; if (tmin > tmax) return null; return infinityrt_vertex_scladd(org, dir, tmin); //var hit = infinityrt_vertex_scladd(org, dir, tmin); //console.log("hit " + hit[0] + "," + hit[1] + "," + hit[2]); //return hit; }; function onSelectStart(event) { var source = event.inputSource; if (source.targetRayMode == "screen") { var fingers = event.frame.getHitTestResultsForTransientInput(xrHitTestSourceForTransient); if (fingers.length === 1) { inputSource = event.inputSource; const gamepad = inputSource.gamepad; var hitPosition = getSceneHit(event.frame.session, scene, gamepad.axes[0], -gamepad.axes[1]); if (hitPosition != null) { isTranslating = true; } else { isRotating = true; fingerLastPosition = fingers[0].inputSource.gamepad.axes; //lastScalar = gamepad.axes[0]; omousev = getCameraCreateRay(event.frame.session, scene, gamepad.axes[0], -gamepad.axes[1]); } } else if (fingers.length === 2) { isScaling = true; lastScalar = fingerSeparation(fingers) / goalScale; } } } //DBG//var debugHitMatrix = null; function getHitPoint(hitResult) { //DBG//debugHitMatrix = null; if (!hitResult) return null; const pose = hitResult.getPose(xrImmersiveRefSpace); if (pose == null) return null; //DBG//const hitMatrix = debugHitMatrix = pose.transform.matrix; const hitMatrix = pose.transform.matrix; //console.log("hit point normal " + hitMatrix[4] + "," + hitMatrix[5] + "," + hitMatrix[6]); // Check that the y-coordinate of the normal is large enough that the normal is pointing up. return hitMatrix[5] > 0.75 ? [hitMatrix[12], hitMatrix[13], hitMatrix[14]] : null; //return [hitMatrix[12], hitMatrix[13], hitMatrix[14]]; } function rotateModel(session, factorX, factorY) { var cammatrix = session.cammatrix; var mousev = getCameraCreateRay(session, scene, factorX, factorY); var camzv = [-cammatrix[8], -cammatrix[9], -cammatrix[10]]; var mvlen = infinityrt_dp(mousev, camzv); mousev = infinityrt_vertex_mul_const(mousev, 1.0 / mvlen); // scale vector so dot product with camera zv is length 1 var deltav = infinityrt_vertex_sub(mousev, omousev); omousev = mousev; var globxv = infinityrt_crossProduct(camzv, [0, 1, 0]); globxv = infinityrt_normalize(globxv); return infinityrt_dp(deltav, globxv); } function processInput(frame) { //DBG//debugHitMatrix = null; if (!isTranslating && !isRotating && !isScaling) { //DBG//DebugStatusLine(frame, -1, null); return; } const fingers = frame.getHitTestResultsForTransientInput(xrHitTestSourceForTransient); if (isScaling) { if (fingers.length < 2) { // If we lose the second finger, stop scaling (in fact, stop processing // input altogether until a new gesture starts). isScaling = false; isRotating = true; return; } // Scaling const separation = fingerSeparation(fingers); const scale = separation / lastScalar; goalScale = (scale < SCALE_SNAP_HIGH && scale > SCALE_SNAP_LOW) ? 1 : scale; } else if (fingers.length === 2) { // If we were rotating or translating and we get a second finger, switch // to scaling instead. isRotating = false; isScalTranslating = true; lastScalar = fingerSeparation(fingers) / goalScale; return; } else if (isTranslating) { // Translating fingers.forEach(finger => { if (finger.inputSource !== inputSource) { //DBG//DebugStatusLine(frame, -2, null); return; } var hit = null; if (finger.results.length > 0) { // Attempt official AR hit point hit = getHitPoint(finger.results[0]); } //DBG//var hitstatus = 1; if (hit == null) { // No official hit point, project one based on raytrace //DBG//hitstatus = 3; // need to be pointing towards the ground const fingerOne = fingers[0].inputSource.gamepad; var ray = getCameraCreateRay(frame.session, scene, fingerOne.axes[0], -fingerOne.axes[1]); if (ray[1] >= -0.1) { //DBG//DebugStatusLine(frame, -3, null); //console.log("Translate failed - ray pointing up " + ray[0] + "," + ray[1] + "," + ray[2]); return; } var cammatrix = frame.session.cammatrix; var campos = [cammatrix[12], cammatrix[13], cammatrix[14]], floory = goalPosition[1]; //intersect with current floor height var dy = floory - campos[1]; var rayfac = dy / ray[1]; if (rayfac <= 0) { //DBG//DebugStatusLine(frame, -4, null); //console.log("Translate failed - intersect with floor invalid, rayfac " + rayfac); return; } var floorhitprojective = [campos[0] + (rayfac * ray[0]), floory, campos[2] + (rayfac * ray[2])]; var hitdist = infinityrt_dist(floorhitprojective, campos); var maxdist = 15; if (hitdist >= maxdist) { //DBG//DebugStatusLine(frame, -5, null); //console.log("Translate failed - hitdist too far " + hitdist); return; } // move object to floorhitprojective hit = floorhitprojective; } //else { //console.log("Valid AR location, use directly"); //} if (saveTranslatingOffset == null) { saveTranslatingOffset = infinityrt_vertex_sub(goalPosition, hit); saveTranslatingOffset[1] = 0.0; //console.log("saveTranslatingOffset " + saveTranslatingOffset[0] + "," + saveTranslatingOffset[1] + "," + saveTranslatingOffset[2]); //DBG//DebugStatusLine(frame, -6, null); return; } goalPosition = infinityrt_vertex_add(saveTranslatingOffset, hit); //goalPosition = hit; //DBG//DebugStatusLine(frame, hitstatus, null); //console.log("Translated " + goalPosition[0] + "," + goalPosition[1] + "," + goalPosition[2] + " hit " + hit[0] + "," + hit[1] + "," + hit[2]); }); } else if (isRotating && fingers.length == 1) { //const thisDragX = inputSource.gamepad.axes[0]; //goalYaw += (thisDragX - lastScalar) * ROTATION_RATE; //lastScalar = thisDragX; //console.log("pt " + inputSource.gamepad.axes[0] + ", " + (-inputSource.gamepad.axes[1])); if(fingerLastPosition[0] === fingers[0].inputSource.gamepad.axes[0] && fingerLastPosition[1] === fingers[0].inputSource.gamepad.axes[1]){ return; } else{ fingerLastPosition = fingers[0].inputSource.gamepad.axes; goalYaw += rotateModel(frame.session, inputSource.gamepad.axes[0], -inputSource.gamepad.axes[1]) * ROTATION_RATE; } //DBG//DebugStatusLine(frame, 2, null); //console.log("rotating... goalYaw " + goalYaw); } var vecAxis, mTmp = MatrixRotationAxis(goalYaw, 0.0, 1.0, 0.0); for (var i = 0; i < scene.children.length; i++) { var sC = scene.children[i]; sC.matrix = MatrixMultiply(sC.matrix, mTmp); vecAxis = infinityrt_normalize([sC.matrix[0], sC.matrix[1], sC.matrix[2]]); vecAxis = infinityrt_vertex_mul_const(vecAxis, goalScale); sC.matrix[0] = vecAxis[0]; sC.matrix[1] = vecAxis[1]; sC.matrix[2] = vecAxis[2]; vecAxis = infinityrt_normalize([sC.matrix[4], sC.matrix[5], sC.matrix[6]]); vecAxis = infinityrt_vertex_mul_const(vecAxis, goalScale); sC.matrix[4] = vecAxis[0]; sC.matrix[5] = vecAxis[1]; sC.matrix[6] = vecAxis[2]; vecAxis = infinityrt_normalize([sC.matrix[8], sC.matrix[9], sC.matrix[10]]); vecAxis = infinityrt_vertex_mul_const(vecAxis, goalScale); sC.matrix[8] = vecAxis[0]; sC.matrix[9] = vecAxis[1]; sC.matrix[10] = vecAxis[2]; if (goalPosition) { sC.matrix[12] = goalPosition[0]; sC.matrix[13] = goalPosition[1] - (scene._bbmin[1] * goalScale); sC.matrix[14] = goalPosition[2]; } sC.matrixDirty = true; } scene.matrixDirty = true; scene._hierarchyDirty = true; goalYaw = 0; } //DBG// //function DebugStatusLine(frame, validhit, pose) { // var cammatrix = frame.session.cammatrix; // if (!cammatrix) // cammatrix = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // if (!posematrix) // posematrix = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // var hitmatrixstr = ""; // if (debugHitMatrix) { // hitmatrixstr = " " + debugHitMatrix[0] + " " + debugHitMatrix[1] + " " + debugHitMatrix[2] + " " + debugHitMatrix[3] + // " " + debugHitMatrix[4] + " " + debugHitMatrix[5] + " " + debugHitMatrix[6] + " " + debugHitMatrix[7] + // " " + debugHitMatrix[8] + " " + debugHitMatrix[9] + " " + debugHitMatrix[10] + " " + debugHitMatrix[11] + // " " + debugHitMatrix[12] + " " + debugHitMatrix[13] + " " + debugHitMatrix[14] + " " + debugHitMatrix[15]; // } // var projmatrix = (scene.xrprojmatrix) ? scene.xrprojmatrix : [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // console.log(cammatrix[0] + " " + cammatrix[1] + " " + cammatrix[2] + " " + cammatrix[3] + // " " + cammatrix[4] + " " + cammatrix[5] + " " + cammatrix[6] + " " + cammatrix[7] + // " " + cammatrix[8] + " " + cammatrix[9] + " " + cammatrix[10] + " " + cammatrix[11] + // " " + cammatrix[12] + " " + cammatrix[13] + " " + cammatrix[14] + " " + cammatrix[15] + // " " + projmatrix[0] + " " + projmatrix[1] + " " + projmatrix[2] + " " + projmatrix[3] + // " " + projmatrix[4] + " " + projmatrix[5] + " " + projmatrix[6] + " " + projmatrix[7] + // " " + projmatrix[8] + " " + projmatrix[9] + " " + projmatrix[10] + " " + projmatrix[11] + // " " + projmatrix[12] + " " + projmatrix[13] + " " + projmatrix[14] + " " + projmatrix[15] + // " " + posematrix[0] + " " + posematrix[1] + " " + posematrix[2] + " " + posematrix[3] + // " " + posematrix[4] + " " + posematrix[5] + " " + posematrix[6] + " " + posematrix[7] + // " " + posematrix[8] + " " + posematrix[9] + " " + posematrix[10] + " " + posematrix[11] + // " " + posematrix[12] + " " + posematrix[13] + " " + posematrix[14] + " " + posematrix[15] + // " " + goalScale + " " + goalYaw + " " + goalPosition[0] + " " + goalPosition[1] + " " + goalPosition[2] + // " " + (isTranslating ? 1 : 0) + " " + (isRotating ? 1 : 0) + // " " + validhit + hitmatrixstr); //} function onSelectEnd(event) { var source = event.inputSource; isRotating = false; isTranslating = false; isScaling = false; saveTranslatingOffset = null; } function fingerSeparation(fingers) { const fingerOne = fingers[0].inputSource.gamepad.axes; const fingerTwo = fingers[1].inputSource.gamepad.axes; const deltaX = fingerTwo[0] - fingerOne[0]; const deltaY = fingerTwo[1] - fingerOne[1]; return Math.sqrt(deltaX * deltaX + deltaY * deltaY); } //var bboxadded = false; function onImmersiveStart() { storedStateStd = { _bDoF: scene._bDoF, activeGlow: scene.activeGlow, _lstNonDepthObjects: scene._lstNonDepthObjects, lstRefractionNodes: scene.lstRefractionNodes, _lstPlanarReflectNodes: scene._lstPlanarReflectNodes, rootMatrices: [], _zNearMin: scene._zNearMin, _rootEnv: scene._rootEnv, _navPan: scene._nav._navPan, _lights: scene._lights }; for (var i = 0; i < scene.children.length; i++) storedStateStd.rootMatrices.push(scene.children[i].matrix.slice(0)); scene.xrActive = true; scene._rootEnv = null; scene._bDoF = false; scene.activeGlow = false; scene._lstNonDepthObjects = []; scene.lstRefractionNodes = []; scene._lstPlanarReflectNodes = []; scene._zNearMin = storedStateStd._zNearMin * goalScale; scene._nav._navPan = [0, 0]; for (var i = 0; i < scene._lights.length; i++) scene._lights[i].data[7] = 0; scene.uniBlockLightsDirty = true; scene.groupSet('ar_view_include', 'visible', 1); // if available 'ar_view_include' (typically for ground shadow) scene.groupSet('ar_view_exclude', 'visible', 0); var bboxexclude = scene._grps["ar_bbox_exclude"]; if (bboxexclude) scene.recalcSceneRadius({ skipInstances: bboxexclude.members, onlyVisible: true }); scene.sceneRadius *= goalScale; if (initParamsXR && initParamsXR.startAR != undefined) initParamsXR.startAR(); //DBG//console.log("bbox " + scene._bbmin[0] + " " + scene._bbmin[1] + " " + scene._bbmin[2] + " " + scene._bbmax[0] + " " + scene._bbmax[1] + " " + scene._bbmax[2]); //DBG//console.log("cammatrix goalScale goalYaw goalPosition isTranslating isRotating validhit (hitmatrix)"); //if (!bboxadded) { // bboxadded = true; // DebugAddBBoxObject(); //} } function onImmersiveEnd() { scene.groupSet('ar_view_include', 'visible', 0); scene.groupSet('ar_view_exclude', 'visible', 1); for (var i = 0; i < scene.children.length; i++) { scene.children[i].matrix = storedStateStd.rootMatrices[i]; scene.children[i].matrixDirty = true; } scene.matrixDirty = true; scene._hierarchyDirty = true; scene._rootEnv = storedStateStd._rootEnv; scene.recalcSceneRadius(); scene._bDoF = storedStateStd._bDoF; scene.activeGlow = storedStateStd.activeGlow; scene._lstNonDepthObjects = storedStateStd._lstNonDepthObjects; scene.lstRefractionNodes = storedStateStd.lstRefractionNodes; scene._lstPlanarReflectNodes = storedStateStd._lstPlanarReflectNodes; scene._zNearMin = storedStateStd._zNearMin; scene._nav._navPan = storedStateStd._navPan; for (var i = 0; i < scene._lights.length; i++) scene._lights[i].data[7] = storedStateStd._lights[i].data[7]; scene.uniBlockLightsDirty = true; scene.xrActive = false; storedStateStd = null; if (initParamsXR && initParamsXR.endAR != undefined) initParamsXR.endAR(); } //function DebugAddBBoxObject() { // var mesh = new infinityrt_object(scene); // mesh.addPoints(new Uint16Array([ // 0, 0, 65535, // 65535, 0, 65535, // 0, 65535, 65535, // 65535, 65535, 65535, // 0, 65535, 0, // 65535, 65535, 0, // 0, 0, 0, // 65535, 0, 0 // ])); // mesh.addNormals(new Int16Array([ // 0, 32761, 0, // 0, 32761, 0, // 0, 32761, 0, // 0, 32761, 0, // 0, 32761, 0, // 0, 32761, 0, // 0, 32761, 0, // 0, 32761, 0 // ])); // mesh.addUVs(new Uint16Array([ // 0, 65535, // 0, 65535, // 0, 65535, // 0, 65535, // 0, 65535, // 0, 65535, // 0, 65535, // 0, 65535 // ])); // mesh._localbbmin = scene._bbmin.slice(0); // mesh._localbbmax = scene._bbmax.slice(0); // mesh.addIndices(new Uint16Array([ // 0, 1, 2, // 2, 1, 3, // 2, 3, 4, // 4, 3, 5, // 4, 5, 6, // 6, 5, 7, // 6, 7, 0, // 0, 7, 1, // 1, 7, 3, // 3, 7, 5, // 6, 0, 4, // 4, 0, 2 // ])); // mesh.setFaceMaterial(scene._Material_ref["Glass_mat"]); // mesh.calcMidPt(); // mesh.loadToCard(scene); // var inst = new infinityrt_instance(scene, { type: 0, s: scene }); // inst.mesh = mesh; // inst.name = "ARBBox"; // scene.children.push(inst); // return inst; //} //////////// Below is Simple API for AR ////////////////////////// //function onPageLoadInit() { // SIRT_AR_Init().then((supported) => { // if (supported) { // var btnAR = document.getElementById("btnAR"); // btnAR.addEventListener("click", function () { // SIRT_AR_Start(); // }); // btnAR.style.display = "inline-block"; // } else { // document.getElementById('imgQR').style.display = "block"; // } // }); //} function detectIpadDevice() { var iPadConfig = {}; iPadConfig.isIPadDevice = false; iPadConfig.dPR = window.devicePixelRatio; iPadConfig.isMacDevice = navigator.userAgent.toLowerCase().indexOf("macintosh") >= 0; iPadConfig.ipadDevice = navigator.userAgent.toLowerCase().indexOf("ipad") >= 0; iPadConfig.iPadWidth = (window.outerWidth == 768 || window.outerWidth == 1024 || window.outerWidth == 1366); iPadConfig.iPadHeight = (window.outerHeight == 768 || window.outerHeight == 1024 || window.outerHeight == 1366); if ((iPadConfig.dPR > 1 && iPadConfig.iPadWidth && iPadConfig.iPadHeight && iPadConfig.isMacDevice) || iPadConfig.ipadDevice) { iPadConfig.isIPadDevice = true; } return iPadConfig.isIPadDevice; } function SIRT_AR_Init() { return new Promise(function (resolve, reject) { SIRT_AR_State.resolve = resolve; if ((navigator.userAgent.indexOf("iPhone") != -1) || (navigator.userAgent.indexOf('iPad') != -1) || (navigator.userAgent.indexOf('iPod') != -1) || (navigator.userAgent == "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.1 Safari/605.1.15") || detectIpadDevice()) { SIRT_AR_State.device = 'usdz'; resolve(true); } else if (navigator.xr) { navigator.xr.isSessionSupported('immersive-ar').then((supported) => { if (supported) { SIRT_AR_State.device = 'webxr'; SIRT_AR_WebXRInit(); } else { resolve(false); } }); } else { resolve(false); } }); } function SIRT_AR_Start(config) { SIRT_AR_State.config = config; if (SIRT_AR_State.device == 'usdz') { const anchor = document.createElement('a'); anchor.setAttribute('rel', 'ar'); anchor.appendChild(document.createElement('img')); anchor.setAttribute('href', SIRT_AR_State.urlRoot + SIRT_AR_State.usdzPath); anchor.click(); } else if (SIRT_AR_State.device == 'webxr') { onRequestSession(); } } function SIRT_AR_WebXRInit() { var canvasSupported = !!window.HTMLCanvasElement; var gl = null; var ua = navigator.userAgent.toLowerCase(); var isAtLeastIE10 = (ua.match(/Trident\/[6]/i)); //test IE10; if (/MSIE (\d+\.\d+);/.test(navigator.userAgent)) { //test for MSIE x.x; var ieversion = new Number(RegExp.$1) // capture x.x portion and store as a number // if (ieversion >= 9 || ieversion >= 8) var IeUser = true; } if (ieversion >= 5 || ieversion >= 6 || ieversion >= 7 || ieversion >= 8 || ieversion >= 9 || isAtLeastIE10) { canvasSupported = false; } var canvasar = document.createElement('canvas'); canvasar.id = "infinityrt-ar-canvas"; canvasar.width = canvasar.height = 16; if (canvasSupported) { gl = infinityrt_getwebglcontext(canvasar, { xrCompatible: true, antialias: true, depth: true }); canvasSupported = !!(window.WebGLRenderingContext && gl); } if (canvasSupported) { var img = document.createElement('img'); img.style.position = 'absolute'; img.style.top = '50%'; img.style.left = '50%'; img.style.transform = 'translateX(-50%) translateY(-50%)'; img.style.maxWidth = '50%'; img.style.maxHeight = '50%'; img.src = SIRT_AR_State.urlRoot + "ar/ar-instructions.png"; var divInstructions = document.createElement('div'); divInstructions.id = 'infinityrt-instructions'; divInstructions.style.opacity = 0; divInstructions.style.display = 'none'; divInstructions.appendChild(img); var divOverlay = document.createElement('div'); divOverlay.id = 'infinityrt-overlay'; divOverlay.appendChild(divInstructions); document.body.appendChild(divOverlay); var urlDataPath = SIRT_AR_State.urlRoot + SIRT_AR_State.rtPath; scene = new infinityrt_scene({ rtgl: gl, useDraco: false, xrOnly: true }, urlDataPath, canvasar.width, canvasar.height, undefined, undefined, undefined, function () { // SetInitialState }, function () { // AllGeometryComplete }); scene._nav = new infinityrt_navigation(scene, canvasar.width, canvasar.height); scene.start(); var scenePollInterval = setInterval(function () { if (scene) { scene.start(); var outstandingJobs = scene.getOutstandingJobs(); if (!(scene._projectparsed)) { } else if (outstandingJobs <= 0 && scene._prepared) { clearInterval(scenePollInterval); end = new Date().getTime(); var time = end - start; console.log('End time: ' + time); SIRT_AR_State.resolve(true); } else if (scene._projectparsed) { scene.setViewMatrix(scene._nav.NavCreateViewMatrix(scene._initialNavMatrix)); scene.setModelMatrix(scene._nav.NavCreateModelMatrix(scene._initialNavMatrix)); scene.draw(); } } }, 100); start = new Date().getTime(); } return canvasSupported; }