Changing parents of a 3D model while keeping the same position - three.js

I'm using three.js library to work with 3d models (mostly .glb but it shouldn't matter)
The idea is to import a 3d model that contains groups and meshes. I want to be able to move meshes between already existing groups within a model without changing the visual representation of the model.
Piece of my code is below. movedInternal is
movedMesh.matrixWorldNeedsUpdate = true; // not sure if it's needed
let meshPosition = new THREE.Vector3();
movedMesh.getWorldPosition(meshPosition);
oldParent.remove(movedMesh);
newParent.add(movedMesh);
movedMesh.worldToLocal(meshPosition);
movedMesh.position.set(meshPosition.x, meshPosition.y, meshPosition.z);
And this is not working. Mesh changes its global position because new parent’s position is not the same as previous parent position, but I expect it to stay where it was but change its local position considering new parent’s position.
What do I do wrong?

I think you need to update the movedMesh's world matrix after you change its parents. When you .remove() then .add(), it doesn't know that its parents have been updated (Three.js usually does it when you're rendering, but this is happening before the next frame render).
let meshPosition = new THREE.Vector3();
movedMesh.getWorldPosition(meshPosition);
oldParent.remove(movedMesh);
newParent.add(movedMesh);
// Here it needs to re-learn its new coordinates
movedMesh.updateMatrixWorld(true);
movedMesh.worldToLocal(meshPosition);
movedMesh.position.set(meshPosition.x, meshPosition.y, meshPosition.z);

I've been searching for this solution since a week and haven't been able to tackle the exact problem. Though my question is diferent but has same scenario, I would like to give a solution for those, looking for it.
I have a 3D object with different child meshes, and i want to replace one of my child mesh with another 3D model.
Below code snippet:
replacementMesh is a new 3D object, meshToReplace is the child mesh that needs to be replaced.
// Copy the transformation matrix from obj1 to obj2
replacementMesh.matrix.copy(meshToReplace.matrix);
// Update obj2's position, rotation, and scale to match the transformation matrix
replacementMesh.position.setFromMatrixPosition(replacementMesh.matrix);
replacementMesh.rotation.setFromRotationMatrix(replacementMesh.matrix);
replacementMesh.scale.setFromMatrixScale(replacementMesh.matrix);
meshToReplace.parent.add(replacementMesh);
meshToReplace.parent.remove(meshToReplace);

Related

How is a Scene Node bound to MeshGeometryModel3D with HelixToolKit

I'm able to load in a model using assimp with:
SceneNode _sceneModel = new Importer().Load(path).Root;
This seems to load in the geometry into its separate meshes - for instance if in Maya you built a room with Wall_1, Wall_2, Wall_3, etc. the SceneNode is some kind of data structure with all the meshes.
The separate meshes can then be put into an array
geometry = _sceneModel.Traverse().Where(x => (x is MeshNode)).Select(m => ((MeshNode)m).Geometry).ToArray();
But ultimately I just want to bind the entire single piece of geometry (eg a house) in xml to
<MeshGeometryModel3D>
but can't figure out how to do it. I can bind a single piece of geometry eg. from the geometry array I can bind geometry[1] which might represent a wall but can't workout how to bind the whole thing.
Thanks
MeshGeometryModel3D is a wpf wrapper for Mesh Node (which is a scene node). If you only want to display your loaded model, you can add the root scene node into a GroupNodeModel3D.
You can find example here
https://github.com/helix-toolkit/helix-toolkit/blob/2254fce0870a165352ccb888e2f3e17398751724/Source/Examples/WPF.SharpDX/FileLoadDemo/MainViewModel.cs#L128

Render single Object / Mesh immediatly into a renderTarget

I need to render a single specific mesh from a scene into a texture using a THREE.WebGLRenderTarget. I already achieved that during the rendering of the scene, all other meshes, except this one, are being ignored. So, I basically achieved my goal. The thing i hate is, that there is still a lot of unnecessary work going on for my whole scene graph, during the render process of the scene. I need to render this texture every frame, so with my current method i get extrem fps drop downs. (There are lots of meshes in the whole scene graph).
So what i found was the function "renderBufferImmediate" from the THREE.WebGLRenderer. (Link to the renderer source code here) My pseudo code to achieve my goal would look like this:
var mesh = some_Mesh;
var renderer = some_WebGLRenderer;
var renderTarget = some_WebGLRenderTarget;
renderer.setRenderTarget(renderTarget);
var materialProperties = renderer.properties.get(mesh.material);
var program = materialProperties.program;
renderer.renderBufferImmediate(mesh, program, mesh.material);
var texture = renderTarget.texture;
The renderBufferImmediate function takes an instance of an THREE.Object3D, a WebGLShaderProgram and a THREE.Material. Problem i see here: The implementation of this function tries to lookup properties of the Object3D which, afaik doesn't exist. (Like "hasPositions" or "hasNormals"). In short: my approach doesn't work.
I would be grateful if someone could tell me if i can use this function for my purpose (Meaning i am currently using it wrong) or if there is another solution for my problem.
Thanks in advance.

Identifying BufferGeometry sides

I'm loading an object from .obj & .mtl files. It looks like this:
What I'm trying to achieve here is that I want to be-able to identify each of the different sides on the bag so I can change each sides colour individually.
I've examined the object and it contains the following:
I've tried to create an array of uniquely coloured materials and apply them like so:
var materials = [
new THREE.MeshPhongMaterial({color: 0x80ffaa}), // Light green
new THREE.MeshPhongMaterial({color: 0xe60000}), // Red
new THREE.MeshPhongMaterial({color: 0xff4dff}), // Pink
new THREE.MeshPhongMaterial({color: 0x3399ff}), // Light blue
new THREE.MeshPhongMaterial({color: 0xac7339}), // Brown
new THREE.MeshPhongMaterial({color: 0x666699}) // Purple
];
var material = new THREE.MeshFaceMaterial(materials)
this.selectedProduct[0]['children'][0].material.materials = materials;
but it appeared like so:
So only three colours were applied to it. The model I'm using is exported out of Blender and so that's where I get my .obj & .mtl files and in the .mtl file it only applies three materials.
So how do I go about getting and updating each side?
EDIT
#pailhead
I've examined the object in Blender and as far as I can see, each of those sides should be unique. Here's the edit view of the object:
So I should be-able to apply a different material to each side then right?
EDIT 2
I'm confused as to what's happening here. I've merged the lip of the bag with the side it was one to create one side each for the bigger sides. Applying the same materials and colour I get this:
The side under each of the handles is one side only - so why am I getting two different colours appearing?
You tried to apply six different materials to a model, and saw only three as a result.
What would have happened if you decided that you needed to provide 7 different materials, or 12 or even 2? The information that should describe whether this model has 3 "different sides" or 6 or 9 is in the .obj file, which does not change no matter how many different materials you apply with three.js.
Is it safe to assume that this particular obj only holds enough information to distinguish between three different faces? I'd say so.
This means that you need to create a different model describing the material relations differently. Go back to blender, assign more ids, or break the mesh apart into "sides" and you'll have logical entities to work with.
Otherwise, you can extract sub meshes out of your geometry. This is complicated but you could build a structure that would allow you to walk the faces. You could then set some kind of thresholds of rules as to where the boundaries should be (like, extract everything that is less than 45 degrees away from the +Y axis, and is connected to face 563).

Three.js Position and visibile issue

Currently, I am trying to dive deeper into grouped objects or better hierachic ordered objects. However I got a strange issue with the position and the visibility of a object3D and childs of those.
So i have a set of objects, a light source with is the main object so far, and a few spheres for example, which are childs of the the main object.
The problem is, that child objects, which are positioned behind another object ( siblings ) ( from the camera view ) are visible. And all childs objects appear infront of the main object while those actually positioned behind the main object.
Sadly i can't reproduce this in a codePen so i had to take some pictures of the scene, many apologies for that.
In the pictures, the camera rotates clockwise around the light source (main object to the left).
So basically what I am doing is:
mObj = new THREE.Object3D();
mObj.add(new THREE.Mesh(someGeometry, someMaterial);
cObj = new THREE.Object3D();
cObj.add(new THREE.Mesh(someGeometry, someMaterial);
mObj.add(cObj);
scene.add(mObj);
Is that related to the object hierachic order or something else?
The second more negligible issue is, that on one of my pc's, those parts of objects which are dark (because of no lighting), generate those strange black/grey faces, which i cant explain. Maybe Graphicscard/driver or something?
What's the distance between these objects? It could be a floating point rounding issue. I've run into it myself.
If it's so, and you're getting flickering models, you'll need to keep your camera and the active model at the origin, and move the universe around you to keep precision in the important places (near the player).

texture mapping different objects in three.js

We are studying on product designer project. Designer is ready.
I want to do 3d preview result with three.js.
How can we texture one side of phone case? or can we border texture mapping?
OBJLoader version:
http://www.shopilife.com/baskiburada/viewer/viewer_4.html
And some obj files cannot be textured. Error is "GL_INVALID_OPERATION : glDrawElements: attempt to access out of range vertices in attribute 2"
http://www.shopilife.com/baskiburada/viewer/viewer2.html
First, regarding re-texturing the back vs. front of the phone case. The approach here is to seperate the UV coordinates on the model itself. This way you have two sets of materials/textures/UVs. Then during runtime you load them both using a MeshFaceMaterial to load the two materials in an array like so:
materialArray.push(THREE.BasicMeshMaterial({color: 0xff0000})); //use whatever Material type you'd like of course
materialArray.push(THREE.BasicMeshMaterial({color: 0x0000ff}));
var multipleMaterial = new THREE.MeshFaceMaterial(materialArray);
phoneCaseMesh= new THREE.Mesh( geometry, multipleMaterial );
Then when you want to switch one out you would grab the mesh and change the mapping to the desired side something like:
phoneCaseMesh.material.materials[1].map = THREE.ImageUtils.loadTexture( 'newtexture.jpg' );
Second, regarding the Error on you second sample, WestLangley is correct the OBJ file has no UV coordinates to map to, so the index is out of bounds when you apply a texture. If you look at both OBJ files your iphone4.obj has vt entities while the untitled.obj is just v and f. Hope that helps

Resources