Hello ^^ A few days ago I started experimenting with <model-viewer> and I was able to show 3D models in MV. This is a geat way to make item inspection more challenging and entertaining.
What does it looks like exactly?
In this tutorial I will show you how to do that using script calls. Though it's more like, I'll explain how to use it and you can decide how you want it to function.
What do you need?
1. A software like Blender 2.8 to convert models to glb or gltf.
2. Photoshop or Gimp to edit textures if needed.
3. At least 6 free variables and 2 common event slots.
This is a pretty text and picture heavy tutorial so I'm putting segments in spoilers.
Adding necessary scripts to project
Getting models ready
Brief explanation of the tag and attributes
Making common events for showing models
Implementation
Important! Please Read- Things to note
Anyways, hope this helped someone even if it looks very tedious. Enjoy ^^
Demo:
What does it looks like exactly?

In this tutorial I will show you how to do that using script calls. Though it's more like, I'll explain how to use it and you can decide how you want it to function.
What do you need?
1. A software like Blender 2.8 to convert models to glb or gltf.
2. Photoshop or Gimp to edit textures if needed.
3. At least 6 free variables and 2 common event slots.
This is a pretty text and picture heavy tutorial so I'm putting segments in spoilers.
Adding necessary scripts to project
First of all, you have to get these two js scrips:
model-viewer.min.js
model-viewer-legacy.js
There are two ways you can handle them in your project:
1. Place them inside your js/plugins/ folder and include them as plugins in the plugin manager:
*Note! Rename model-viewer.min to model-viewer-min or model-Viewer_min.

Or
2. Link these plugins directly to the index.html file:

It’s better to use a code editing software for this. Notepad++ should work fine.
There is not much difference between these two but I like to separate dependency scripts and put them directly in the html file as it helps to keep my plugin manager cleaner and only list plugins I can modify.
model-viewer.min.js
model-viewer-legacy.js
There are two ways you can handle them in your project:
1. Place them inside your js/plugins/ folder and include them as plugins in the plugin manager:
*Note! Rename model-viewer.min to model-viewer-min or model-Viewer_min.

Or
2. Link these plugins directly to the index.html file:

It’s better to use a code editing software for this. Notepad++ should work fine.
There is not much difference between these two but I like to separate dependency scripts and put them directly in the html file as it helps to keep my plugin manager cleaner and only list plugins I can modify.
Getting models ready
Let’s get a glb model for testing. For this tutorial I just went to archive3D and got a globe. It came in a bunch of formats but what we need is a .glb or a .gltf model. So convert it if your model doesn’t come in that format. And always keep notes where you got the model from.
If you have Microsoft’s 3D viewer installed, the model viewer can be converted to a glb model without any hassle. But if you don’t, you can use Blender. Blender 2.8+ has built-in support for glTF files.
I just used 3D viewer.
*Note! The model must be fairly large.
How large I can’t really say since I sometimes use pmx editor for quick edits and it’s a very simple editor so I have no idea about the scale. But I usually scale it up like, 10x, 100x, 10x XD So, very large. Before you scale your models though, load it up and see if you can see a red block (if you follow my instructions and put in the exact same style I will give you later on). If you can’t, then your model needs scaling.
Put your glb model in a separate folder just to keep your project files organized.
I put this globe in a new folder in the root folder named 'models'.
If you have Microsoft’s 3D viewer installed, the model viewer can be converted to a glb model without any hassle. But if you don’t, you can use Blender. Blender 2.8+ has built-in support for glTF files.
I just used 3D viewer.
*Note! The model must be fairly large.
How large I can’t really say since I sometimes use pmx editor for quick edits and it’s a very simple editor so I have no idea about the scale. But I usually scale it up like, 10x, 100x, 10x XD So, very large. Before you scale your models though, load it up and see if you can see a red block (if you follow my instructions and put in the exact same style I will give you later on). If you can’t, then your model needs scaling.
Put your glb model in a separate folder just to keep your project files organized.
I put this globe in a new folder in the root folder named 'models'.
Brief explanation of the tag and attributes
Before we actually start working, let’s take a look at the <model-viewer> html tag. I’ll explain what each attribute does. And for those who have no idea what I’m talking about:
Here,
<div></div> ---> Opening and closing tag
id, style ---> Attribute
width, height ---> Property
For the <model-viewer> tag, we’ll use this setting:
id = The name/identity that’s given to an element. Having an id is very useful as you can use it to control different properties of the element from the outside via javascript.
style = The css style of the element. There are many properties that we can assign using the style attribute but we will mainly use width, height, left and top. I’ll come to this later.
rotation-per-second = How many times the model will rotate per second. The unit is degree or radian.
loading = The loading type of the model. “Eager” makes the model show as soon as it’s loaded. “Lazy” doesn’t show the model till it’s interacted it. Default is lazy but we will use eager.
interaction-prompt = Whether there should be a little hand pointing to the model to tell the user to interact with it or not. I set this to “none” but if you want it you can set it to “auto”
auto-rotate = turns on auto rotation. Doesn’t accept values.
auto-rotate-delay = How long should the model wait before starting to rotate. I use 0.
src = The source of the model. In my case its inside the models folder that I made so the source is “models/model_name.glb”
The other ones don’t really matter so I won’t explain them.
We will also use buttons to create clickable areas in the model. So that when the player inspects them, different common events can be triggered. Let’s look at the button tag and its attributes:
onClick = This is the common event/js function to be triggered when the button/hotspot is clicked.
slot = This is sort of like id. It will give the button an identity and the button will follow the model as it moves. This MUST start with hotspot- otherwise it won’t work. Example: hotspot-hand (a clickable area in the hand of the model)
data-position = The x,y and z coordinates that we will use to position the hotspot. You will need to play around with the values to find the perfect one for your model.
style = same as the style attribute used in the model-viewer tag. This will let us change the size and colour of the buttons/clickable areas.
data-normal = Not really sure what this does. But it’s necessary so don’t leave it blank. Just put in whatever 0 0 1, 1 0 0, 0 0 0 whatever you want.
You’ll see what type of values to insert in all the attributes from examples I will show you later.
HTML:
<div id="test" style="width: 100%; height: 100%;"></div>
<div></div> ---> Opening and closing tag
id, style ---> Attribute
width, height ---> Property
For the <model-viewer> tag, we’ll use this setting:
HTML:
<model-viewer id="" rotation-per-second="" loading="" interaction-prompt= "" ar="" auto-rotate auto-rotate-delay="" ar-modes="" camera-controls="" src="" data-js-focus-visible="" ar-status="">
</model-viewer>
style = The css style of the element. There are many properties that we can assign using the style attribute but we will mainly use width, height, left and top. I’ll come to this later.
rotation-per-second = How many times the model will rotate per second. The unit is degree or radian.
loading = The loading type of the model. “Eager” makes the model show as soon as it’s loaded. “Lazy” doesn’t show the model till it’s interacted it. Default is lazy but we will use eager.
interaction-prompt = Whether there should be a little hand pointing to the model to tell the user to interact with it or not. I set this to “none” but if you want it you can set it to “auto”
auto-rotate = turns on auto rotation. Doesn’t accept values.
auto-rotate-delay = How long should the model wait before starting to rotate. I use 0.
src = The source of the model. In my case its inside the models folder that I made so the source is “models/model_name.glb”
The other ones don’t really matter so I won’t explain them.
We will also use buttons to create clickable areas in the model. So that when the player inspects them, different common events can be triggered. Let’s look at the button tag and its attributes:
HTML:
<button onClick="" slot="" data-position="" data-normal="">
</button>
slot = This is sort of like id. It will give the button an identity and the button will follow the model as it moves. This MUST start with hotspot- otherwise it won’t work. Example: hotspot-hand (a clickable area in the hand of the model)
data-position = The x,y and z coordinates that we will use to position the hotspot. You will need to play around with the values to find the perfect one for your model.
style = same as the style attribute used in the model-viewer tag. This will let us change the size and colour of the buttons/clickable areas.
data-normal = Not really sure what this does. But it’s necessary so don’t leave it blank. Just put in whatever 0 0 1, 1 0 0, 0 0 0 whatever you want.
You’ll see what type of values to insert in all the attributes from examples I will show you later.
Making common events for showing models
Make a new div in the body section of index.html:
You can name the id however you want but make sure to change the #model in the style to #yourPrefereredName (see below)
Add this to the head section of index.html:
Make a common event. We’ll make id and src attributes. Why? To make this thing universal. So that you can show any model you like without making countless model-viewer tags.
Script call:
How it looks like:

Also, let's make another common event for showing hotspots. We need this because you might need to make multiple buttons for one model, let alone several other models. Are we manually going to make these buttons? No. We will generalize it with a common event.
What it looks like:

To delete a model, you don’t need to make a common event as it’s just one line of code whenever you want it to not be on the screen anymore.
Now the model is permanently deleted, freeing up space.
HTML:
<div id="model"></div>
Add this to the head section of index.html:
HTML:
<style>
#model{
z-index: 7;
}
model-viewer {
width: 100%;
height: 100%;
position: fixed;
outline: none;
position: fixed;
--poster-color: transparent;
z-index: 7;
}
button{
display: block;
width: 20px;
height: 20px;
border-radius: 10px;
border: none;
background-color: blue;
box-sizing: border-box;
}
:not(:defined) > * {
display: none;
}
</style>
Script call:
JavaScript:
var loadModel = function(var1){
var model= `<model-viewer id="`+var1+`" style="width: 100%; height: 100%; left:0%; top: 0%;" rotation-per-second="30deg" loading="eager" interaction-prompt= "auto" ar="" auto-rotate auto-rotate-delay=0 ar-modes="webxr" camera-controls="" src="models/`+var1+`.glb" data-js-focus-visible="" ar-status="not-presenting">
</model-viewer>`
return model;
}
document.getElementById("model").innerHTML += loadModel($gameVariables.value(1)); //Change model to whatever you set as the div's id.
//Feel free to change the variable ID to something else.

Also, let's make another common event for showing hotspots. We need this because you might need to make multiple buttons for one model, let alone several other models. Are we manually going to make these buttons? No. We will generalize it with a common event.
JavaScript:
var loadHotspots = function(var2, var3, var4, var5){
var buttons =`<button onClick="$gameTemp.reserveCommonEvent(`+var4+`)" slot="`+var5+`" data-position="`+var2+`" data-normal="`+var3+`"></button>`
return buttons;
}
document.getElementById($gameVariables.value(1)).innerHTML += loadHotspots($gameVariables.value(2), $gameVariables.value(3), $gameVariables.value(4), $gameVariables.value(5));
//Feel free to change the variable IDs used.

To delete a model, you don’t need to make a common event as it’s just one line of code whenever you want it to not be on the screen anymore.
JavaScript:
document.getElementById("<model you used in Variable 1 to show the model>").remove();
Implementation
Let's make an event:

What did I do here? I set the model's name and source which is stored in Variable 1 to 'globe'. That's the name of my model which is in the models folder. If you feel that you need two different isntances of the same model, the you just need to assign a different variable, say, variable 6 as id. I just didn't see the need for it.
I also set the data position, data-normal, triggered common event ID and slot name. Slot name MUST begin with 'hotspot-'
The I called the show model common event. Then I called buttons common event.
*Note! You can set multiple hotspots. you just need to change the variable values 2-5 before calling buttons common event.
After 200 frames, the model is deleted.
Well then, let's see how it looks like in-game:

Too big! It's even covering our text box! (Which we triggered by clicking the red box).
Let's change that.
Back to our event! Add this just under the buttons common event:

Now it looks like this:

To much to the left! Let's position it. Back to the event!
Add this:

Now what does it look like?

What if I add another button?

Phew! We are mostly done. But not yet. Want to make the red box transparent so that the player doesn't know where to click?
Change the css we put in the index.html file:
I made the background transparent and now if you hover over the block, you'll see a different icon. Delete it if you don't want that. You can also control the width and height of the button here.

What did I do here? I set the model's name and source which is stored in Variable 1 to 'globe'. That's the name of my model which is in the models folder. If you feel that you need two different isntances of the same model, the you just need to assign a different variable, say, variable 6 as id. I just didn't see the need for it.
I also set the data position, data-normal, triggered common event ID and slot name. Slot name MUST begin with 'hotspot-'
The I called the show model common event. Then I called buttons common event.
*Note! You can set multiple hotspots. you just need to change the variable values 2-5 before calling buttons common event.
After 200 frames, the model is deleted.
Well then, let's see how it looks like in-game:

Too big! It's even covering our text box! (Which we triggered by clicking the red box).
Let's change that.
Back to our event! Add this just under the buttons common event:
JavaScript:
var model = document.getElementById($gameVariables.value(1));
model.style.width = "70%";
model.style.height = "70%";

Now it looks like this:

To much to the left! Let's position it. Back to the event!
Add this:
JavaScript:
model.style.left = "15%";
model.style.top = "10%";

Now what does it look like?

What if I add another button?

Phew! We are mostly done. But not yet. Want to make the red box transparent so that the player doesn't know where to click?
Change the css we put in the index.html file:
HTML:
button{
display: block;
width: 40px;
height: 40px;
background-color: transparent;
background-repeat:no-repeat;
border: none;
cursor:url(icon/magnify.png), auto;
overflow: hidden;
outline:none;
}
Important! Please Read- Things to note
1. The models might look very blurry when loaded. If your game runs on <50 fps, the model's texture gets auto resized. I haven't found a way to stop that. So the best way to show crisp models would be to transfer the player to a very minimal map or take a screenshot of the current map and use it as a picture in a completely empty map. I'm not sure if it will happen to everyone but I use gridfree doodads, lighting plugins and shadow plugins, my screen resolution is huge (1920x1080) so it's possible that it won't happen to you.
2. The map doesn't pause when you load the model. So use a plugin to stop map movement till it is removed or the player will keep moving when you move the model and vice versa.
I have a simple fix for this though.
stop all input:
Enable all input:
3. If you don't see the hotspots, your model is too tiny. Scale it up by a few tens.
Edit 18/09/2020:
4. If you don't want to enable zooming in and out, edit the model-viewer_min and legacy js files to get rid of all event listeners that listen for the wheel. Or you can just replace them with these:
model-viewer_min.js
model-viewer-legacy.js
2. The map doesn't pause when you load the model. So use a plugin to stop map movement till it is removed or the player will keep moving when you move the model and vice versa.
I have a simple fix for this though.
stop all input:
JavaScript:
if($gamePlayer.moveByInputCached === undefined)
$gamePlayer.moveByInputCached = $gamePlayer.moveByInput;
$gamePlayer.moveByInput = function(){
return false;
}
JavaScript:
$gamePlayer.moveByInput = $gamePlayer.moveByInputCached;
Edit 18/09/2020:
4. If you don't want to enable zooming in and out, edit the model-viewer_min and legacy js files to get rid of all event listeners that listen for the wheel. Or you can just replace them with these:
model-viewer_min.js
model-viewer-legacy.js
Anyways, hope this helped someone even if it looks very tedious. Enjoy ^^
Demo:

Last edited: