THREE.js repeating UV texture using JSONLoader - three.js

I need help for getting UV Textures to be rendered correctly in three.js. I've created a model with repeating tile texture in Blender. The tile texture is applied using UV mapping, and it looks like this if it is being rendered correctly:
Render image using Blender
.However, when it is loaded using JSONLoader by three.js, the tiles are just stretched to fill each polygon, giving weird result like this:Screenshot of render using three.js
. I've tried setting THREE.RepeatWrapping in my code but nothing changed:
bodyLoader = new THREE.JSONLoader();
bodyLoader.load('./starofthesea_threejs.json', function(geometry, materials) {
mainBodyMaterials = new THREE.MeshFaceMaterial(materials);
console.log(materials);
mainBodyMaterials.wrapS = THREE.RepeatWrapping;
mainBodyMaterials.wrapT = THREE.RepeatWrapping;
mainBodyMaterials.needsUpdate = true;
mainBody = new THREE.Mesh(geometry, mainBodyMaterials);
mainBody.traverse ( function (child) {
if (child instanceof THREE.Mesh) {
child.castShadow = true;
child.receiveShadow = true;
}
});
mainBody.scale.x = mainBody.scale.y = mainBody.scale.z = 1;
geometry.computeBoundingBox();
geometry.computeFaceNormals();
geometry.computeFlatVertexNormals();
scene.add(mainBody);
});
Is there anything wrong in my code, or workaround to get it rendered correctly? All help is deeply appreciated.

Finally I've figured out the problem by myself, where both the Blender model and JS are misconfigured. RepeatWrapping should be applied to texture but not material. I need to study the structure of THREE.MeshFaceMaterial to find the handle for the underlying textures. I need to traverse through the materials to find out all materials with image textures:
mainBodyMaterials = new THREE.MeshFaceMaterial(materials);
for(prop in mainBodyMaterials.materials) {
if(mainBodyMaterials.materials[prop].map != null) {
mainBodyMaterials.materials[prop].map.wrapS = THREE.RepeatWrapping;
mainBodyMaterials.materials[prop].map.wrapT = THREE.RepeatWrapping;
tex.push(mainBodyMaterials.materials[prop].map.clone());
tex[tex_sequence].needsUpdate = true;
tex_sequence++;
}
}
After applying wrapS and wrapT to textures correctly, one of the tile materials get rendered correctly, but 'Texture marked for update but image is undefined' error keep throwing out. I need to clone the texture to get rid of the error, according to another question: Three.js r72 - Texture marked for update but image is undefined?
As there are several materials with repeating tiles, I need to create a global array at the beginning of the JS routine, and push the modified textures one by one when looping through the materials:
var tex = new Array();
var tex_sequence = 0;
After fixing the JS calls, one of the textures are still not working. I forgot that only ONE UV slot is allowed for three.js. I need to unwrap the UVs again under the same UV slot in Blender. Everything works like a charm, and I hope my painful experience can help those who are getting mad by similar problems.

Related

Dynamically change textures in three js for imported gltf model

No matter what I do, I cannot get a texture to update after importing a GLTF model using react-three fiber.
const { nodes, materials } = useGLTF("/glb4.glb");
const newtexture = useLoader(TextureLoader, "texture1.jpg");
newtexture.flipY = false;
Now I can do
let newmaterial = new THREE.MeshPhysicalMaterial({ map: newtexture});
to import it into the scene with
<mesh
geometry={nodes.Body_Front_Node.geometry}
material={newmaterial}
/>
However it results in a grey material without the texture. Even modifying the original gltf material with the new texture gives the same result.
Is there a way to update the texture of a gltf mode dynamically? Here is the full codesandbox: https://codesandbox.io/p/github/Mazzz-zzz/fabrigen/main?file=%2Fsrc%2FApp.js

Convert MeshStandardMaterial to MeshBasicMaterial for glb model

I am loading a .glb model into Three.js. All the materials get turned into MeshStandardMaterial. How can I convert them to MeshBasicMaterial?
The goal is to get the exact colors that I used in Blender without having to use any lights in the Three.js scene.
Attempts
(1) I have tried the following code, based on https://stackoverflow.com/a/61717988/795016, but the problem is that this approach appears not to work when going from a more complex to a less complex material.
// For each child in the group
loadedGroup.traverse((child) => {
// If this child has a mesh standard material
if (
child instanceof THREE.Mesh
&& child.material instanceof THREE.MeshStandardMaterial
) {
var prevMaterial = child.material;
child.material = new THREE.MeshBasicMaterial();
THREE.MeshStandardMaterial.prototype.copy.call(child.material, prevMaterial);
console.log("child.material", child.material);
}
});
(2) I have also tried to use the gltf-transform tool to change the materials in the model to be unlit, but Three.js still shows them as MeshStandardMaterial.
gltf-transform unlit model-in.glb model-out.glb

Aframe Load lightmap after loading GLTF - lightmap not showing

I'm trying to add a lightmap to some mesh after loading them from a GLTF file.
All my objects have 2UV channel.
I'm waiting 'object3dset' and here is my code :
const mesh = this.el.getObject3D('mesh');
var textureLoader = new THREE.TextureLoader();
textureLoader.load("lightmap.png", function(lmap){
mesh.traverse((node) => {
if (!node.isMesh) return;
node.material.lightMap = lmap;
lmap.flipY = node.material.map.flipY; //needed to flip the texture
node.material.needsUpdate = true;
});
});
If I replace the material with a new one and set the lightmap, it's working.
But I want to find a way without recreating all materials.
The lightmap was loaded, but not easy to see.
By default metalness from Khronos Blender Exporter converted in threejs after loading GLTF result to a level 1.0. With this configuration, the lightmap is hard to see and is not corresponding to what we see in Blender.
I hope my mistake can help someone else losing too much time.

Animations stretching geometry

I read that the latest threejs exporter added support for multiple animations so I created a quick experiment and exported a model with the latest plugin..after a bit of playing around with the code I managed to get each of the animations within the model playing, however the geometry is getting skewed horribly.
I'm pretty sure the issue is related to the following error: "gl.getProgramInfoLog() WARNING: Could not find vertex shader attribute 'position' to match BindAttributeLocation request." but not positive. Maybe I'm just not aware of something, such as a particular blender export flag configuration or something else that needs to be set within the code. Here's the important parts:
for (var i = 0; i < materials.length; i++) {
var mat = materials[i];
mat.skinning = true;
}
scene.add(mesh);
// add animation data to the animation handler
var animations = [];
for(var a in mesh.geometry.animations) {
THREE.AnimationHandler.add(mesh.geometry.animations[a]);
var animation = new THREE.Animation(mesh,mesh.geometry.animations[a].name);
animations.push(animation);
}
animations[0].play();
Any ideas guys? Here's a screenshot: https://drive.google.com/file/d/0B6zdGNflpdJLSVVHemszWWFTTHc/edit?usp=sharing

Loading multiple objects in Three.js from Blender

I have a quite complex shape (dressed girl) that in Blender is broken down into different objects and it's loaded into Three.js with the JSON loader (with a little hack I made, that uses zipped files instead of just JSON files, as there are a lot of vertices).
As I want to change dynamically the style of the dress from a Web page, I was wondering how I can show/hide different pieces (e.g. sleeves) in the scene.
I tried traversing the THREE.Mesh, but there are no children.
When I export from Blender with the JSON exporter, I don't see anything referring to the names of the objects in the JSON. Is the structure lost?
If you are using meshes containing lots of vertices, I would advise you to use the openCTM webGL loader instead of zip hacking. Here is a link to the loader : http://threejs.org/examples/webgl_loader_ctm.html
This mesh compression tool uses LZMA compression method and can reduce the size of your files by 93%...
Concerning the JSONLoader, using an array might help:
var meshes = [];
...
var loader = new THREE.JSONLoader();
var onGeometry = function(geom)
{
var mesh = new THREE.SceneUtils.createMultiMaterialObject(geom, [material]);
meshes.push( mesh );
...
scene.add(mesh);
};
loader.load("yourfile.js", onGeometry);
Hope this helps
It is possible to load an entire scene with several meshes from a json file exported from Blender and handle them separately!
You can follow the complete process of exporting a entire scene from Blender and the right way of handling the exported meshes on my answer of this post.
So, you can get the different meshes and manipulate them separately using the getObjectByName method. But it is important to know that the loaded object isn't a Geometry anymore. It is labeled with the Scene type by now and it must be handled in a different way.
The loading code must look like this one:
loader = new THREE.JSONLoader();
loader.load( "obj/Books.json", function ( loadedObj ) {
var surface = loadedObj.getObjectByName("Surface");
var outline = loadedObj.getObjectByName("Outline");
var mask = loadedObj.getObjectByName("Mask");
scene.add(surface);
scene.add(outline);
scene.add(mask);
} );
Besides, you can handle the multiple materials of single mesh using THREE.MeshFaceMaterial like in the following code:
var mat1 = new THREE.MeshLambertMaterial( { map: texture1 } );
var mat2 = new THREE.MeshLambertMaterial( { map: texture2 } );
var materials = [mat1, mat2];
var faceMat = new THREE.MeshFaceMaterial(materials);
mesh = new THREE.Mesh( geometry, faceMat );
scene.add( mesh );
Hope this helps :)

Resources