I am a student with an upcoming project using Google’s model-viewer to render a poster using AR on a user’s wall. I have a simple, textureless, one-sided plane that I made using the three.js online editor, which lives in my project’s directory. I am trying to implement a system where the texture of that plane can be set to an image to simulate a poster/painting/etc.
Google’s model-viewer documentation has an example (the one with the rubber duck) in which a model has a texture created and set from a referenced image file, and I have attempted to implement it on my end using the plane and some various images of different types in place of their examples, but I cannot get it to work.
I have been looking for possible solutions online for a long time with no real progress so I figured it was time to ask the question for myself. The documentation makes this look simple enough so clearly, I’m misunderstanding something.
Here is the modified code from the model-viewer documentation:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Model-Viewer Test</title>
<script type="module" src="https://unpkg.com/@google/model-viewer/dist/model-viewer.min.js"></script>
<style>
model-viewer {
width: 400px;
height: 600px;
}
</style>
</head>
<body>
<model-viewer id="plane" camera-controls src="assets/blank.glb" ar ar-modes="webxr">
<div class="controls">
<p>Textures</p>
<select id="normals2">
<option>None</option>
<option value="assets/planet.jpeg">Forbidden Planet</option>
<option value="assets/retro.jpg">Retro Camera</option>
<option value="assets/movie.png">Movie Poster</option>
</select>
</div>
</model-viewer>
<script type="module">
const modelViewerTexture = document.querySelector("model-viewer#plane");
modelViewerTexture.addEventListener("load", () => {
const material = modelViewerTexture.model.materials[0];
const createAndApplyTexture = async (channel, event) => {
if (event.target.value == "None") {
// Clears the texture.
material[channel].setTexture(null);
} else if (event.target.value) {
// Creates a new texture.
const texture = await modelViewerTexture.createTexture(event.target.value);
// Set the texture name
texture.name = event.target.options[event.target.selectedIndex].text.replace(/ /g, "_").toLowerCase();
// Applies the new texture to the specified channel.
material[channel].setTexture(texture);
}
}
document.querySelector('#normals2').addEventListener('input', (event) => {
createAndApplyTexture('normalTexture', event);
});
});
</script>
</body>
</html>
The only response I get when picking an option in the selector is the error message:
Uncaught (in promise) TypeError: Cannot set property name of #<xy> which has only a getter
at createAndApplyTexture
which refers to line 46:
texture.name = event.target.options[event.target.selectedIndex].text.replace(/ /g, "_").toLowerCase();
and otherwise, nothing happens. The plane itself is rendering with no problems. I have tried using both a .gltf and a .glb model.
I do not intend to plagiarize from Google’s documentation on the actual project as I have done here, I just wanted to see if I could get the dynamic textures working beforehand and immediately hit a wall.
Thank you to anyone who sees or responds to this.