Click events on 3D Objects

We need to collect the data as the user changes the material of the object in the scene. Are there any possibilities to detect that click event on which user clicks to change the material of an object?

It is possible to detect from JavaScript which material was selected by the user in the material picker, but looking at the code I don’t see an easy way to detect which objects have the new material applied.

Material picker allows to replace all occurrences of one material in the scene with other material from the scene. Because objects with the same material in the scene are merged together for performance reasons, there is no easy way to determine which logical object was affected by the change. For example if a floor and a table use the same wood material, they will be merged together into a single object.

Hey Jan,

Thanks for your quick response.
Can you please tell me how to detect which material was selected by the user?
Can you please point me to any tutorial or any example using which i can understand how to do that?

We don’t yet have tutorial on this. You can start from placing a following body-end.html file in Documents\Shapespark\SCENE_NAME: Detect which material was selected by the material picker · GitHub

Material picker uses an setMaterialForNode API call, and this code snippet intercepts such calls to print to the console which material was selected.

Hi jan,

Thanks for your help. Now we have to test it on our own local server first. Could you please tell us how to do it? Because shapespark is not allowing us to upload it with body-end.html file. We saw on one of the links that you have to whitelist us to access this feature. Here is the link: Configurator Commands - #4 by jan
Username is cningoo

And we have found one more solution here : Hosting scenes on own servers , with the Bundle option, enabled.

So which one is the best method to approach?

You can test locally by connecting to http://localhost:5000 with your browser while Shapespark is running. To upload body-end.html, you need at least the Standard plan. The reason is that during trial period and with the Starter plan, custom branding is disabled and body-end.html allows to brand the viewer.

It works like a Charm…!!

Thank you so much for the quick help…!!

Hi jan,

Is there any update going to come, in which we can change the 3d models at run time?
Like for example if we want to see different sofa option at run time.

Is there any possibility of it?

Hi @cningoo, we don’t work on this now, but very likely will be adding such feature this year.

3 Likes

Hello @jan
Is it feasible in the webgl combined render? Only for individual objects - dynamic lighting and shadows?

Hi @Vladan What do you mean by the combined render? The editable objects feature that @cningoo asks about is not available yet. When it is added, it will most likely be done gradually, first the editable objects won’t be casting shadows, but we will be experimenting with dynamic lighting that works well with architectural scenes and WebGL.

Hi @jan , is it possible to put labels on material picker, when selecting materials thru API like this:

or any other way to put the labels on material picker, thank you!

@Half_stack it is not possible to add labels, but please see this post: Material name under Sample - #5 by jan for an explanation how to add a text popup with a selected material name.

1 Like

Thanks @jan , I think that would be the path I’m gonna follow cause it would be easy.

But I would just like to ask, I also did a custom menu, using the material picker api, please see screenshot:

but I encountered some issues, so I stop working it out lol:
-like making the dimensions of the textures with the multiple of 64px, cause when I use a different dimension it render the texture dark.
-and when I tried the following code by adding roughness, metallic, bump., it stops working
var material = viewer.findMaterial(areaToChange);
material.baseColorTexture = texture;
material.roughnessTexture = 0.02;
material.metallicTexture = 1;
material.bumpTexture = 0.01;

Thank you @jan really appreciate the help

Looks really nice!

WebGL requires textures that repeat (like floor tiles) to have power of 2 dimensions. Textures that do not repeat are not required to be power of two, but it looks like the API doesn’t configure such textures correctly. Until we have a fix for this you will see a black result even for not repeating textures.

Anyway, having all textures sizes power of two is the best approach. You can use ImageMagick to resize textures:

convert some-texture.jpg -resize 512x512  some-texture_power_of_two.jpg

To assign scalar values, do not use the Texture suffix:

material.roughness = 0.02;
material.metallic = 1.0;
material.bumpScale = 0.01; // This only has effect if material.bumpTexture is set.

For now, after such changes you will need to call:

material.setUniforms();
1 Like

Jan, regarding the power of two.
How about adding the image upscale function for non-power of two images (in case of the equal width and height)?
I have added a custom override function to the WALK.TextureLoader
Here is the code that seems to be working for me.

function upscaleImg(a) {
var targetWidth = nextHighestPowerOfTwo(a.width);
var targetHeight = nextHighestPowerOfTwo(a.height);
var canvas = document.createElement(“canvas”);
canvas.width = targetWidth;
canvas.height = targetHeight;
var ctx = canvas.getContext(“2d”);
ctx.drawImage(a.image, 0, 0, targetWidth, targetHeight);
a.image = canvas;
ctx = null;
canvas = null;
console.log("image was upscaled to " + targetWidth + "px by " + targetHeight);
}

function nextHighestPowerOfTwo(x) {
–x;
for (var i = 1; i < 32; i <<= 1) {
x = x | x >> i;
}
return x + 1;
}

I think we can add such automatic scaling to the API call. It has slight disadvantage of hiding this operation from the API user (such scaling is not super cheap, so whenever possible using already scaled textures is better).

I’m only using it for the customized material picker function. In my case, I have a lot of materials that need to be loaded dynamically, after the Scene initialization. They all have resolution of 800x800px. I know that I can do the server-side scaling for the images, but I prefer for the browsers to do the work to reduce unnecessary server load.
May be if upscaling would be an optional feature, that would be nice. I agree that for the static materials there is no point of doing that.

@jan

Really appreciate the help, just want to ask where do I upload the textures? currently they are on my local machine.

Also, for this one, : Detect which material was selected by the user and show the material name. · GitHub
Is there a way to show the material.basetextures, instead of the material.name ?

Thank you again!

@Half_stack the use case that we had in mind for createTextureFromHTMLImage is to allow setting of textures from external sources (for example, hosted on user’s server). Shapespark doesn’t support uploading of images that are not part of the scene.

You can show material.baseTexture.name. If you would like to show an image, you could do it rather only with a custom material picker like the one in your screen shot. Texture is an object that is renderable in WebGL, it is not easy to transform such texture back to an HTML Image element and display it as a part of HTML UI. Your custom UI could keep a reference to the original HTML image, and use the original image to show the selected texture to the user.

1 Like