function krpanoplugin() { var local = this; var krpano = null; var device = null; var plugin = null; local.registerplugin = function (krpanointerface, pluginpath, pluginobject) { krpano = krpanointerface; device = krpano.device; plugin = pluginobject; if (krpano.version < "1.19") { krpano.trace(3, "ThreeJS plugin - too old krpano version (min. 1.19)"); return; } if (!device.webgl) { // show warning krpano.trace(2, "ThreeJS plugin - WebGL required"); return; } krpano.debugmode = true; krpano.trace(0, "ThreeJS krpano plugin"); // load the requiered three.js scripts // load_scripts(["three.min.js"], start); load_scripts(["three.js", "GLTFLoader.js"], start); } local.unloadplugin = function () { // no unloading support at the moment plugin = null; krpano = null; } local.onresize = function (width, height) { return false; } function resolve_url_path(url) { if (url.charAt(0) != "/" && url.indexOf("://") < 0) { // adjust relative url path url = krpano.parsepath("%CURRENTXML%/" + url); } return url; } function load_scripts(urls, callback) { if (urls.length > 0) { var url = resolve_url_path(urls.splice(0, 1)[0]); var script = document.createElement("script"); script.src = url; script.addEventListener("load", function () { load_scripts(urls, callback); }); script.addEventListener("error", function () { krpano.trace(3, "loading file '" + url + "' failed!"); }); document.getElementsByTagName("head")[0].appendChild(script); } else { // done callback(); } } // helper var M_RAD = Math.PI / 180.0; // ThreeJS/krpano objects var renderer = null; var scene = null; var camera = null; var stereocamera = null; var camera_hittest_raycaster = null; var krpano_panoview = null; var krpano_panoview_euler = null; var krpano_projection = new Float32Array(16); // krpano projection matrix var krpano_depthbuffer_scale = 1.0001; // depthbuffer scaling (use ThreeJS defaults: znear=0.1, zfar=2000) var krpano_depthbuffer_offset = -0.2; var sceneMeshes = [] function start() { // create the ThreeJS WebGL renderer, but use the WebGL context from krpano renderer = new THREE.WebGLRenderer({ canvas: krpano.webGL.canvas, context: krpano.webGL.context, antialias: false }); renderer.autoClear = false; renderer.setPixelRatio(1); // krpano handles the pixel ratio scaling // restore the krpano WebGL settings (for correct krpano rendering) restore_krpano_WebGL_state(); // use the krpano onviewchanged event as render-frame callback (this event will be directly called after the krpano pano rendering) krpano.set("events[__threejs__].keep", true); krpano.set("events[__threejs__].onviewchange", adjust_krpano_rendering); // correct krpano view settings before the rendering krpano.set("events[__threejs__].onviewchanged", render_frame); // enable continuous rendering (that means render every frame, not just when the view has changed) krpano.view.continuousupdates = true; // register mouse and touch events if (device.browser.events.mouse) { krpano.control.layer.addEventListener("mousedown", handle_mouse_touch_events, true); } if (device.browser.events.touch) { krpano.control.layer.addEventListener(device.browser.events.touchstart, handle_mouse_touch_events, true); } // basic ThreeJS objects scene = new THREE.Scene(); camera = new THREE.PerspectiveCamera(450, window.innerWidth / window.innerHeight, 1, 1000);// new THREE.Camera() // camera.position.z = 1; stereocamera = new THREE.Camera(); camera_hittest_raycaster = new THREE.Raycaster(); krpano_panoview_euler = new THREE.Euler(); // build the ThreeJS scene (start adding custom code there) build_scene(); // restore the krpano WebGL settings (for correct krpano rendering) restore_krpano_WebGL_state(); } function restore_krpano_WebGL_state() { var gl = krpano.webGL.context; gl.disable(gl.DEPTH_TEST); gl.cullFace(gl.FRONT); gl.frontFace(gl.CCW); gl.enable(gl.BLEND); gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); gl.activeTexture(gl.TEXTURE0); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); gl.pixelStorei(gl.UNPACK_ALIGNMENT, 4); // restore the current krpano WebGL program krpano.webGL.restoreProgram(); renderer.resetState() //renderer.resetGLState(); } function restore_ThreeJS_WebGL_state() { var gl = krpano.webGL.context; gl.enable(gl.DEPTH_TEST); gl.depthFunc(gl.LEQUAL); gl.enable(gl.CULL_FACE); gl.cullFace(gl.BACK); gl.clearDepth(1); gl.clear(gl.DEPTH_BUFFER_BIT); renderer.resetState() // renderer.resetGLState(); } function krpano_projection_matrix(sw, sh, zoom, xoff, yoff) { var m = krpano_projection; var pr = device.pixelratio; sw = pr / (sw * 0.5); sh = pr / (sh * 0.5); m[0] = zoom * sw; m[1] = 0; m[2] = 0; m[3] = 0; m[4] = 0; m[5] = -zoom * sh; m[6] = 0; m[7] = 0; m[8] = xoff; m[9] = -yoff * sh; m[10] = krpano_depthbuffer_scale; m[11] = 1; m[12] = 0; m[13] = 0; m[14] = krpano_depthbuffer_offset; m[15] = 1; } function update_camera_matrix(camera) { var m = krpano_projection; camera.projectionMatrix.set(m[0], m[4], m[8], m[12], m[1], m[5], m[9], m[13], m[2], m[6], m[10], m[14], m[3], m[7], m[11], m[15]); } function adjust_krpano_rendering() { if (krpano.view.fisheye != 0.0) { // disable the fisheye distortion, ThreeJS objects can't be rendered with it krpano.view.fisheye = 0.0; } } function render_frame() { var gl = krpano.webGL.context; var vr = krpano.webVR && krpano.webVR.enabled ? krpano.webVR : null; var sw = gl.drawingBufferWidth; var sh = gl.drawingBufferHeight; // setup WebGL for ThreeJS restore_ThreeJS_WebGL_state(); // set the camera/view rotation krpano_panoview = krpano.view.getState(krpano_panoview); // the 'krpano_panoview' object will be created and cached inside getState() krpano_panoview_euler.set(-krpano_panoview.v * M_RAD, (krpano_panoview.h - 90) * M_RAD, krpano_panoview.r * M_RAD, "YXZ"); camera.quaternion.setFromEuler(krpano_panoview_euler); camera.updateMatrixWorld(true); // set the camera/view projection krpano_projection_matrix(sw, sh, krpano_panoview.z, 0, krpano_panoview.yf); update_camera_matrix(camera); // do scene updates update_scene(); // render the scene if (krpano.display.stereo == false) { // normal rendering renderer.setViewport(0, 0, sw, sh); renderer.render(scene, camera); } else { // stereo / VR rendering sw *= 0.5; // use half screen width var stereo_scale = 0.05; var stereo_offset = Number(krpano.display.stereooverlap); // use a different camera for stereo rendering to keep the normal one for hit-testing stereocamera.quaternion.copy(camera.quaternion); stereocamera.updateMatrixWorld(true); // render left eye var eye_offset = -0.03; krpano_projection_matrix(sw, sh, krpano_panoview.z, stereo_offset, krpano_panoview.yf); if (vr) { eye_offset = vr.eyetranslt(1); // get the eye offset (from the WebVR API) vr.prjmatrix(1, krpano_projection); // replace the projection matrix (with the one from WebVR) krpano_projection[10] = krpano_depthbuffer_scale; // adjust the depthbuffer scaling krpano_projection[14] = krpano_depthbuffer_offset; } // add the eye offset krpano_projection[12] = krpano_projection[0] * -eye_offset * stereo_scale; update_camera_matrix(stereocamera); renderer.setViewport(0, 0, sw, sh); renderer.render(scene, stereocamera); // render right eye eye_offset = +0.03; krpano_projection[8] = -stereo_offset; // mod the projection matrix (only change the stereo offset) if (vr) { eye_offset = vr.eyetranslt(2); // get the eye offset (from the WebVR API) vr.prjmatrix(2, krpano_projection); // replace the projection matrix (with the one from WebVR) krpano_projection[10] = krpano_depthbuffer_scale; // adjust the depthbuffer scaling krpano_projection[14] = krpano_depthbuffer_offset; } // add the eye offset krpano_projection[12] = krpano_projection[0] * -eye_offset * stereo_scale; update_camera_matrix(stereocamera); renderer.setViewport(sw, 0, sw, sh); renderer.render(scene, stereocamera); } // important - restore the krpano WebGL state for correct krpano rendering restore_krpano_WebGL_state(); } // ----------------------------------------------------------------------- // ThreeJS User Content - START HERE var clock = null; var animatedobjects = []; var box = null; // add a krpano hotspot like handling for the 3d objects function assign_object_properties(obj, name, properties) { // set defaults (krpano hotspot like properties) // if (properties === undefined) properties = {}; if (properties.name === undefined) properties.name = name; if (properties.ath === undefined) properties.ath = 0; if (properties.atv === undefined) properties.atv = 0; if (properties.depth === undefined) properties.depth = 1000; if (properties.scale === undefined) properties.scale = 1; if (properties.rx === undefined) properties.rx = 0; if (properties.ry === undefined) properties.ry = 0; if (properties.rz === undefined) properties.rz = 0; if (properties.rorder === undefined) properties.rorder = "YXZ"; if (properties.enabled === undefined) properties.enabled = true; if (properties.capture === undefined) properties.capture = true; if (properties.onover === undefined) properties.onover = null; if (properties.onout === undefined) properties.onout = null; if (properties.ondown === undefined) properties.ondown = null; if (properties.onup === undefined) properties.onup = null; if (properties.onclick === undefined) properties.onclick = null; properties.pressed = false; properties.hovering = false; properties.scale = obj.scale obj.properties = properties; update_object_properties(obj); } function update_object_properties(obj) { var p = obj.properties; var px = p.depth * Math.cos(p.atv * M_RAD) * Math.cos((180 - p.ath) * M_RAD); var py = p.depth * Math.sin(p.atv * M_RAD); var pz = p.depth * Math.cos(p.atv * M_RAD) * Math.sin((180 - p.ath) * M_RAD); obj.position.set(px, py, pz); obj.rotation.set(p.rx * M_RAD, p.ry * M_RAD, p.rz * M_RAD, p.rorder); // obj.scale.set(p.scale, p.scale, p.scale); obj.updateMatrix(); } function load_object_json(url, animated, properties, donecall) { url = resolve_url_path(url); var loader = new THREE.JSONLoader(); loader.load(url, function (geometry, materials) { var material = materials[1]; if (animated) { material.morphTargets = true; material.morphNormals = true; geometry.computeMorphNormals(); } geometry.computeVertexNormals(); var obj = new THREE.MorphAnimMesh(geometry, material); if (animated) { obj.duration = 1000; obj.time = 0; // obj.matrixAutoUpdate = false; animatedobjects.push(obj); } assign_object_properties(obj, url, properties); scene.add(obj); if (donecall) { donecall(obj); } }); } function load_object_gltf(url, properties, donecall = null) { url = resolve_url_path(url); return new Promise((resolve, reject) => { let loader = new THREE.GLTFLoader(); loader.load(url, function (gltf) { gltf.scene.traverse(function (child) { if (child.geometry) { child.geometry.computeBoundingBox(); child.geometry.computeBoundingSphere(); } if (child.isMesh) { const { geometry, material } = child; geometry.computeVertexNormals(); var obj = new THREE.Mesh(geometry, material); child.material.transparent = true; child.material.metalness = 0; child.visible = true; child.castShadow = true; child.receiveShadow = true; // obj.updateMatrixWorld() // material.side = THREE.BackSide; ///THREE.DoubleSide // sceneMeshes.push(child) // child.geometry.computeBoundingBox(); // const boundingBox = new THREE.Box3().setFromObject(child); // child.scale.set(-1, 0, 0); // console.log(child); assign_object_properties(child, url, properties); // child.position.set(10,10, 10) // child.rotation.set(-Math.PI / 180, Math.PI / 180, -Math.PI, "YXZ") } }); // gltf.scene.rotation.set(Math.PI / 180, -Math.PI / 180, Math.PI); // gltf.scene.position.x *= -1 // gltf.scene.position.y *= -1 // gltf.scene.position.z *= -1 scene.add(gltf.scene); // gltf.scene.position.set(-1, -1, -1) gltf.scene.scale.set(-1, -1, -1) resolve(gltf.scene); }) }) } console.log(); function build_scene() { clock = new THREE.Clock(); load_object_gltf("fileName.glb", { ath: 0, atv: 0, depth: 0, scale: 1, rx: 0, ry: 0, rz: 0, ondown: function (obj) { }, onup: function (obj) { } }) // add scene lights scene.add(new THREE.AmbientLight(0xFFFFFF)); var directionalLight = new THREE.DirectionalLight(0xFFFFFF); directionalLight.position.x = 0.5; directionalLight.position.y = -1; directionalLight.position.z = 0; directionalLight.position.normalize(); scene.add(directionalLight); // scene.traverse((o) => { if (o.geometry) { o.geometry.computeBoundingBox(); o.geometry.computeBoundingSphere(); } }); console.log("scenee", scene,); } function do_object_hittest(mx, my) { var mouse_x = -(mx / krpano.area.pixelwidth) * 2.0 + 1.0; var mouse_y = (my / krpano.area.pixelheight) * 2.0 - 1.0; var krpano_view = krpano.view; var vlookat_rad = krpano_view.vlookat * Math.PI / 180; mouse_y += Math.tan(vlookat_rad) * 2.0; if (krpano.display.stereo) { mouse_x += (mouse_x < 0.0 ? +1 : -1) * (1.0 - Number(krpano.display.stereooverlap)) * 0.5; } scene.updateMatrixWorld() camera_hittest_raycaster.ray.direction.set(mouse_x, mouse_y, -1.0).unproject(camera).normalize(); camera_hittest_raycaster.ray.direction.x *= -1 camera_hittest_raycaster.ray.direction.z *= -1 scene.add(new THREE.ArrowHelper(camera_hittest_raycaster.ray.direction, camera_hittest_raycaster.ray.origin, 300, 0xff0000)); var intersects = camera_hittest_raycaster.intersectObjects(scene.children, true); var i; var obj; for (i = 0; i < intersects.length; i++) { obj = intersects[i].object; if (obj && obj.properties && obj.properties.enabled) { return obj; } } return null; } var handle_mouse_hitobject = null; function handle_mouse_touch_events(event) { var type = ""; if (event.type == "mousedown") { type = "ondown"; // handle_mouse_click(event) krpano.control.layer.addEventListener("mouseup", handle_mouse_touch_events, true); } else if (event.type == "mouseup") { type = "onup"; krpano.control.layer.removeEventListener("mouseup", handle_mouse_touch_events, true); } else if (event.type == device.browser.events.touchstart) { type = "ondown"; krpano.control.layer.addEventListener(device.browser.events.touchend, handle_mouse_touch_events, true); } else if (event.type == device.browser.events.touchend) { type = "onup"; krpano.control.layer.removeEventListener(device.browser.events.touchend, handle_mouse_touch_events, true); } // get mouse / touch pos var ms = krpano.control.getMousePos(event.changedTouches ? event.changedTouches[0] : event); ms.x /= krpano.stagescale; ms.y /= krpano.stagescale; // is there a object as that pos? var hitobj = do_object_hittest(ms.x, ms.y); if (type == "ondown") { if (hitobj) { handle_mouse_hitobject = hitobj; hitobj.properties.pressed = true; if (hitobj.properties.ondown) { hitobj.properties.ondown(hitobj); } if (hitobj.properties.capture) { krpano.mouse.down = true; console.log(hitobj) } event.preventDefault(); } } else if (type == "onup") { if (handle_mouse_hitobject && handle_mouse_hitobject.properties.enabled) { if (handle_mouse_hitobject.properties.pressed) { handle_mouse_hitobject.properties.pressed = false; if (handle_mouse_hitobject.properties.onup) { handle_mouse_hitobject.properties.onup(handle_mouse_hitobject); } } if (handle_mouse_hitobject.properties.onclick) { if (hitobj == handle_mouse_hitobject) { handle_mouse_hitobject.properties.onclick(handle_mouse_hitobject); } } } krpano.mouse.down = false; } } function handle_mouse_hovering() { if (krpano.mouse.down == false) // currently not dragging? { var hitobj = do_object_hittest(krpano.mouse.x, krpano.mouse.y); if (hitobj != handle_mouse_hitobject) { if (handle_mouse_hitobject) { handle_mouse_hitobject.properties.hovering = false; if (handle_mouse_hitobject.properties.onout) handle_mouse_hitobject.properties.onout(handle_mouse_hitobject); } if (hitobj) { hitobj.properties.hovering = true; if (hitobj.properties.onover) hitobj.properties.onover(hitobj); } handle_mouse_hitobject = hitobj; } if (handle_mouse_hitobject) {//&& (krpano.display.stereo == false && krpano.display.hotspotrenderer != "webgl")) { krpano.control.layer.style.cursor = krpano.cursors.hit; } else { krpano.cursors.update(); } } } function update_scene() { handle_mouse_hovering(); } }