You are not logged in.

1

Wednesday, March 29th 2023, 9:08am

Add plugin dynamicaly via Javascript - krpanoplugin : local variable error

Hi,

i try add plugin to krpano dynamicaly via JavaScript.

My code :

function krpanoplugin() {
var local = this; // save the 'this' pointer from the current plugin object

var krpano = null; // the krpano and plugin interface objects
var device = null;
var plugin = null;
....
// same code from documentation
}

than i call :

krpano.call("addplugin(test);");
krpanoo.set("plugin[test].url", krpanoplugin());

but i get error : Uncaught TypeError: local is undefined.

can someone help me why in Javascript cant access pointer from the current plugin object ?

Many thanks guys ..

kme

Intermediate

Posts: 261

Location: Belgium

Occupation: Long time coder, product manager and 3D enthousiast

  • Send private message

2

Wednesday, March 29th 2023, 10:28am

Hi,

even though i dont have an answer to your question right now, i m curious to understand what your goal is for loading the plugin dnymically, as maybe there is another/easier way to achieve your goal.

gr,
kristof

3

Wednesday, March 29th 2023, 10:35am

Hi kme,


I wanted to use a freely available plugin (https://krpano.com/plugins/threejs/opensource/).

My goal is to activate this plugin dynamically, i.e. after clicking the button.

But I need to be able to change the objects that are being loaded. I don't want to load them all at once, but change them to an action - the user clicks on a button. Thus, an example:

1. the tour is loaded.
2. the user clicks on the button (add bird model). Now the krpano plugin must be loaded dynamically, in which only the bird will be defined.
3. later the user clicks add wooden box, the bird is removed and the stork is added.

The goal and necessity is to use this solution via Javascript. Because the objects that the user could add will change dynamically - loaded from the database. The available solution (https://krpano.com/forum/wbb/index.php?p…ad&postID=60149) provides the possibility to define in advance which objects will be added.

However, I have to be able to control it via Javascript and only if the user really wants it.

kme

Intermediate

Posts: 261

Location: Belgium

Occupation: Long time coder, product manager and 3D enthousiast

  • Send private message

4

Wednesday, March 29th 2023, 11:36am

i would approach that a bit different. i would load the plugin, maybe even create the scene, but not load any objects in it. and then create a seperate add/delete for each object you want to load and call those from within krpano.

but thats just my 2c *rolleyes*

good luck with the project and keep us informed on the progress...

kme

5

Wednesday, March 29th 2023, 3:55pm

Source code

1
plugin[test].url = function() { ... }

assigning a function to an url doesn't work.
normally the function(s) would be defined inside the plugin
and you load the plugin by setting its url (with the path to the plugins js file)
but if you have the function already, why load a plugin anyway ?
*confused*

This post has been edited 2 times, last edit by "indexofrefraction" (Mar 29th 2023, 4:10pm)


6

Wednesday, March 29th 2023, 4:49pm

Hi indexofrefraction,

i need call tihs function (from JS script) in pure javaScript file, no via XML (include plugin) :

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);
}

local.unloadplugin = function()
{
// no unloading support at the moment
}

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;


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});
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.Camera();
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.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.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;

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[0];

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 build_scene()
{
clock = new THREE.Clock();

// load 3d objects
load_object_json("monster.js", true, {ath:+30, atv:+15, depth:500, scale:0.1, rx:180, ry:60 ,rz:0, ondown:function(obj){ obj.properties.scale *= 1.2; update_object_properties(obj); }, onup:function(obj){ obj.properties.scale /= 1.2; update_object_properties(obj); }});
load_object_json("flamingo.js", true, {ath:-110, atv:-20, depth:700, scale:1.0, rx:-10, ry:250, rz:180, ondown:function(obj){ obj.properties.scale *= 1.2; update_object_properties(obj); }, onup:function(obj){ obj.properties.scale /= 1.2; update_object_properties(obj); }});
load_object_json("horse.js", true, {ath:-58, atv:+7, depth:1000, scale:1.0, rx:180, ry:233, rz:0, ondown:function(obj){ obj.properties.scale *= 1.2; update_object_properties(obj); }, onup:function(obj){ obj.properties.scale /= 1.2; update_object_properties(obj); }}, function(obj){ obj.material.color.setHex(0xAA5522); } );
}


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;

if (krpano.display.stereo)
{
mouse_x += (mouse_x < 0.0 ? +1 : -1) * (1.0 - Number(krpano.display.stereooverlap)) * 0.5;
}

camera_hittest_raycaster.ray.direction.set(mouse_x, -mouse_y, 1.0).unproject(camera).normalize();

var intersects = camera_hittest_raycaster.intersectObjects( scene.children );
var i;
var obj;

for (i=0; i < intersects.length; i++)
{
obj = intersects.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";
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;
event.stopPropagation();
}

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()
{
// check mouse over state
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()
{
// animate objects
var delta = clock.getDelta();

if (box)
{
box.properties.rx += 50 * delta;
box.properties.ry += 10 * delta;
update_object_properties(box);
}

for (var i=0; i < animatedobjects.length; i++)
{
animatedobjects[i].updateAnimation(1000 * delta);
}

handle_mouse_hovering();
}
}

7

Wednesday, March 29th 2023, 8:23pm

so add this to your xml

Source code

1
<plugin name="myplugin" url="path/to/your/plugin.js" keep="true" />


or you add it to a scene without keep=true
but without a proper unloadplugin() function your threejs stuff will not unload as it should

kme

Intermediate

Posts: 261

Location: Belgium

Occupation: Long time coder, product manager and 3D enthousiast

  • Send private message

8

Wednesday, March 29th 2023, 8:59pm

I have modified the sample a bit to demonstrate some of the things you can do to proceed.

1) load the plugin as indexofrefraction says:

Source code

1
	<layer name="threejs" preload="true" url="three.krpanoplugin.js" keep="true"/>

2) I added 2 javascript functions inside the plugin:

In registerplugin:

Source code

1
2
		plugin.addobject = addobject;
		plugin.removeobject = removeobject;


further down:

Source code

1
2
3
4
5
6
7
8
9
	function addobject(url) {
		load_object_json(url,    true, {ath:-58,  atv:+7,  depth:1000, scale:1.0, rx:180, ry:233, rz:0,   ondown:function(obj){ obj.properties.scale *= 1.2; update_object_properties(obj); }, onup:function(obj){ obj.properties.scale /= 1.2; update_object_properties(obj); }}, function(obj){ obj.material.color.setHex(0xAA5522); } );
	}
	
	function removeobject(url) {
		url = resolve_url_path(url);
		var selectedObject = scene.getObjectByName(url);
		scene.remove(selectedObject);		
	}


3) as we need to remove elements from the scene, we have to give them a name, so I added the following in the load_object_json() function:

Source code

1
line 394:			obj.name=url;

3) I removed the loading of the horse in build_scene()

4) I added 2 buttons to the krpano scene:

Source code

1
2
3
	<layer name="addhorse" onclick="layer[threejs].addobject('horse.js')" align="bottom" y="20" width="100" height="30" type="text" text="add horse"></layer>

	<layer name="removehorse" onclick="layer[threejs].removeobject('horse.js')" align="bottom" y="20" x="120" width="100" height="30" type="text" text="remove horse"></layer>


5) upgraded to the latest version of krpano

I have attached these modifications here too.
(could not attach, file too big, here is the project: https://krpano.kri-soft.be/krpano_three_js_example_kme.rar )
This demonstrates:
a) how to add functions that you can call from within krpano actions
b) how to load/unload objects from the scene

What you should do from here
- make the "addobject()" function more dynamic (now it only takes the url/name of the object, but you need parameters for the complete loading (location, textures, ...)
- add code to cleanup your plugin (it's not done yet in this plugin)
- better upgrade to the latest version of three.js too

I hope I have given you some insight in how to approach this.

km

9

Thursday, March 30th 2023, 1:15pm

Hi kme,

many thank for this example. all work correctly.
I would need one last thing. is it possible to use such a function to add own THREE.js things to the krpano scene? for example geometries, boxes, meshes...

my idea is that I will send the code for THREE.js to the function via Javascript, which will draw, for example, a box like this: https://codepen.io/discountry/pen/jVgjaE but on the krpano scene.

So, the ideal situation = to have a function with which I insert any code for THREE.js and it will then generate the required code after the action is started, example : :

in my JS code:

function test() {

/* threejs code */
box = new THREE.Mesh(new THREE.BoxGeometry(500,500,500), new THREE.MeshBasicMaterial({map:THREE.ImageUtils.loadTexture(resolve_url_path("box.jpg"))}));
assign_object_properties(box, "box", {ath:160, atv:-3, depth:2000, ondown:function(obj){ obj.properties.scale *= 1.2; }, onup:function(obj){ obj.properties.scale /= 1.2; }});
scene.add( box );
}

and then I call:

krpano.call("plugin[threejs].golive(" + test + ")");

would it be possible ?

This post has been edited 1 times, last edit by "rostlerkk" (Mar 30th 2023, 1:28pm)


cuongdevjs

Beginner

Posts: 9

Location: Viet Nam

Occupation: JavaScript Engineer

  • Send private message

10

Saturday, April 1st 2023, 4:19am

Sorry, can i ask? Have anyone tried to load gltf model? I tried and had shown it on screen but the events such as ondown and onup doesn't happen. Thank you. *smile*

kme

Intermediate

Posts: 261

Location: Belgium

Occupation: Long time coder, product manager and 3D enthousiast

  • Send private message

11

Saturday, April 1st 2023, 7:17am

There is a zip file in this threat that loads a glb. Not sure if it has support for mouse events, but maybe it can help you anyways

https://discourse.threejs.org/t/webgl-00…er-size/39547/4