I am loading a .obj file that builds a human dummy, and inside the .obj file there are lines that show where the meshes split E.G. "g "body_part" followed by the vertices, and faces. When I load my object it works great, and I can console log the object and see the children which are all the different meshes that build the object. The problem is that when I am using raycaster to change the color of the mesh that is hovered it will only work on specific parts. After further investigation I found that when the object is being loaded it is generating duplicates of the same mesh. Does anyone have an explaination on why it is duplicating duplicate meshes or a solution to this?
A hack to get the raycasting to work when hovering over a mesh is to choose the last index of the array returned by intersection, but this is a hack and does not fix the duplication problem. I am still investigating.
// store reference to closest object as current intersection object
this.INTERSECTED = intersects[intersects.length - 1].object;
Also, if I log the intersects it returns the correct mesh being intersected, but it returns all of the duplicates with it as well. For example when I hover over a hand it returns an array of the same object except they have different uids:
(9) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
0
:
{distance: 158.11485222188028, point: Vector3, object: Mesh, uv: Vector2, face: Face3, …}
1
:
{distance: 158.11485222188028, point: Vector3, object: Mesh, uv: Vector2, face: Face3, …}
2
:
{distance: 158.11485222188028, point: Vector3, object: Mesh, uv: Vector2, face: Face3, …}
3
:
{distance: 158.11485222188028, point: Vector3, object: Mesh, uv: Vector2, face: Face3, …}
4
:
{distance: 158.11485222188028, point: Vector3, object: Mesh, uv: Vector2, face: Face3, …}
5
:
{distance: 158.11485222188028, point: Vector3, object: Mesh, uv: Vector2, face: Face3, …}
6
:
{distance: 158.11485222188028, point: Vector3, object: Mesh, uv: Vector2, face: Face3, …}
7
:
{distance: 158.11485222188028, point: Vector3, object: Mesh, uv: Vector2, face: Face3, …}
8
:
{distance: 158.11485222188028, point: Vector3, object: Mesh, uv: Vector2, face: Face3, …}
length
:
9
__proto__
:
Array(0)
Loading object:
loadModel = (objModel) => {
//ADD Obj instantiate a loader
this.THREE = THREE;
const objLoader = new this.THREE.OBJLoader();
this.obj;
// load(URL, callback, loading, on error)
objLoader.load(objModel,
//called when resource is loaded
(object) => {
object.traverse( function ( child ) {
if ( child instanceof THREE.Mesh ) {
if(child.name == "dummy_hips3 dummy_belly dummy_torso dummy_rshoulder dummy_rarm dummy_relbow dummy_rfarm dummy_rwrist dummy_rhand") {
console.log(child) <-- This will log 8x, and it should only happen once
}
child.material = new THREE.MeshBasicMaterial({color: 0x444444, wireframe: true});
}
});
object.position.y = -100;
this.obj = object;
console.log(object)
this.scene.add(object);
},
//called when loading is in progresses
(xhr) => {
this.setState({
percentageLoaded: (xhr.loaded / xhr.total * 100).toFixed(2) + '%'
});
},
//called when loading has errors
(error) => {
console.log(error);
});
}
Raycasting function
findObjIntersection = (obj) => {
// find intersections
// create a Ray with origin at the mouse position
// and direction into the scene (camera direction)
var vector = new THREE.Vector3(this.state.mouse.x, this.state.mouse.y, 1);
vector.unproject(this.camera);
var raycaster = new THREE.Raycaster(this.camera.position, vector.sub(this.camera.position).normalize());
// create an array containing all objects in the scene with which the ray intersects
// You can also pass this.scene.children for other objects
var intersects = raycaster.intersectObjects(obj);
// INTERSECTED = the object in the scene currently closest to the camera
// and intersected by the Ray projected from the mouse position
// if there is one (or more) intersections
if (intersects.length > 0) {
// if the closest object intersected is not the currently stored intersection object
if (intersects[0].object != this.INTERSECTED) {
// restore previous intersection object (if it exists) to its original color
if (this.INTERSECTED) this.INTERSECTED.material.color.setHex(this.INTERSECTED.currentHex);
// store reference to closest object as current intersection object
console.log(intersects)
this.INTERSECTED = intersects[0].object;
// store color of closest object (for later restoration)
this.INTERSECTED.currentHex = this.INTERSECTED.material.color.getHex();
// set a new color for closest object
this.INTERSECTED.material.color.setHex(0xff0000);
this.setState({
INTERSECTED: this.INTERSECTED.name
});
}
} else { // there are no intersections
// restore previous intersection object (if it exists) to its original color
if (this.INTERSECTED) this.INTERSECTED.material.color.setHex(this.INTERSECTED.currentHex);
// remove previous intersection object reference
// by setting current intersection object to "nothing"
this.INTERSECTED = null;
this.setState({
INTERSECTED: 'False'
});
}
}
I am showing what mesh is intersected, and it turns red as it is supposed to but only when I am in certain spot hovering the arm and I need it to happen when any of the arm is being hovered.
Here is the second image where the arm is being hovered, and it is detecting the intersection and displaying that it is indeed the same arm, but since I am not hovering over a specific spot it is returning a duplicate mesh that has a different id and this is where the problem is.
My guess is that the duplicates are in your obj file. The raycaster returns every object hit by the ray, so if you're getting 8 hits, there are 8 copies of the geometries there.
Are you exporting from Blender? There may be other copies in another layer, or hidden?
edit: I downloaded your model and viewed it in blender. The geometry is duplicated Up to 6 times for some segments of the object. This is not an uncommon problem because as a modeller, you may not notice the duplicates because they are identical copies within the same mesh.
Mesh obj size went from like 2.5 megs, down to ~350 k :)
In blender, you can go into edit mode on an object, select a single vertex, then hit Ctrl-L (select linked vertices) and then move that chunk of geometry, and it reveals a duplicate left behind.
The good news is that once these dupes are stripped out, your model file is going to be much much smaller. :)
eidt:1.5 I found an easier way to clean this model up.. selecting all the parts, then space bar->split->Separate by loose parts.. that turns each duped mesh into a separate object that was easy to select and get rid of.
edit2: Here's the cleaned up OBJ file with dupes removed.
https://drive.google.com/open?id=1Oi-hA9biuZmYuQ6ehiISOUZqCJDxd_fi
Related
I need to set the Sprite(Hello) position to stay with the mesh...Now, the Sprite is independent. If I rotate the object, the Sprite should stay with the mesh
LATER EDIT:
Mesh position is always 0..
I tried for couple of hours but nothing worked..
Here is a screenshot of the code:
And the code...
loader.load('/3D_Cars/Dacia_1410_Tuned/components/scene.gltf', function (gltf) {
carModel = gltf.scene.children[0];
gltf.scene.traverse(function (child) {
if (child.isMesh) {
var v = new THREE.Vector3(0, 0, 0);
domEvents.addEventListener(carModel.getObjectByName(child.name), 'click', function (event) {
app.RenameAsCarParts(child.name)
console.log(child.name)
}, false)
if (child.name == 'rim_RSL1_Aluminium_0') {
child.position.set(100, 50, 3); //testing
var spritey = makeTextSpriteNew(" Hello ",
{ fontsize: 15, textColor: { r: 0, g: 0, b: 0, a: 1.0 } });
scene.add(spritey);
}
}
});
carModel.getObjectByName('Body_paint_0').material = bodyMaterial;
carModel.rotation.set(Math.PI / 2, 0, 0);
carModel.rotateY(Math.PI);
scene.add(carModel);
});
Thank you!
Where is this method defined makeTextSpriteNew?
You can refer this three.js example for drawing Text Sprites in three.js
https://stemkoski.github.io/Three.js/Sprite-Text-Labels.html
The idea is, you need to provide a 3d position to this sprite so that it will project that position on screen and display the Sprite.
In your case you need to provide it the mesh's position (bounding box corner/center based on where you need to show the sprite).
spritey.position.set(posX, posY, posZ);
I would like to rotate the coordinate axes of specific segments of a .fbx model. E.g. as illustrated on the image I would like to rotate the coordinate axes of the right arm 90 degress around the Z-axis.
I can see that THREE.Object3D.DefaultUp can change the default coordinate system of an object, but I cannot apply it to the fbx segment objects.
Just to be clear, I dont want to rotate the mesh/arm, but only the coordinate axes that define the arms rotation. Is that possible?
I load the .fbx model and define the arm as so:
// model
const loader = new THREE.FBXLoader();
loader.load( '{{ url_for('static', filename='ybot.fbx') }}', function ( object ) {
myObj = object;
myObj.traverse( function ( child ) {
if ( child.isMesh ) {
child.castShadow = true;
child.receiveShadow = true;
}
});
rightArm = myObj.getObjectByName('mixamorigRightArm');
// Do the rotations here
scene.add( myObj );
});
}
I am trying to add the edge lines to the 3D object with Three.js. Here is the object loaded:
Here is the code I used to draw the edges:
// geometry is the object loaded from remote server
const edgeGeometry = new EdgesGeometry( geometry );
const edgeMaterial = new LineBasicMaterial( { color: 0x333333, linewidth: 0.5 } );
const edgeWireframe = new LineSegments( edgeGeometry, edgeMaterial );
scene.add(edgeWireframe );
The result is like this:
This the goal I am trying to achieve:
Only the edges have the lines. The question is: How can I draw the edge lines without these lines on the curve surfaces? Thank you for your time.
I'm importing 3D models into three.js using MTLLoader and OBJLoader. The .obj files contain no normals so the resulting models look blocky. I'm attempting to smooth them by converting the BufferGeometry returned by OBJLoader into a Geometry object, and then calculating the normals. This works perfectly for many objects, but some of them turn almost completely black.
Investigating this, I noticed that on the models that turn black the vertex normals (computed by computeVertexNormals()) are (0, 0, 0) for almost every vertex, while on the normal-looking models they are decimal values between -1 and 1.
Here is my code:
function loadModel() {
// Load the materials first
mtlLoader.load('texture.mtl', function (materials) {
materials.preload();
// Load the object and attach the materials
objLoader
.setMaterials(materials)
.load('model.obj', function (object) {
object.children[0].geometry = new THREE.Geometry().fromBufferGeometry(object.children[0].geometry);
object.children[0].geometry.computeFaceNormals();
object.children[0].geometry.mergeVertices();
object.children[0].geometry.computeVertexNormals();
scene.add(object);
}, undefined, function (error) {
console.log(error);
});
}, undefined, function (error) {
console.log(error);
}
);
}
How can I fix/prevent this?
I want to adapt this Shader here:
https://aerotwist.com/tutorials/an-introduction-to-shaders-part-2/
to a standard Lambert or Phong that it works with all my Lights in the Scene.
My current state is that I extend the Lambert with this code:
var attributes = {
displacement: {
type: 'f', // a float
value: [] // an empty array
}
};
var uniforms = {
amplitude: {
type: 'f', // a float
value: 0
}
};
var shaders = { mylambert : THREE.ShaderLib[ 'lambert' ] };
var materials = {};
materials.mylambert = function( parameters, myUniforms ) {
var material = new THREE.ShaderMaterial( {
vertexShader: $('#vertexLambert').text(),
fragmentShader: shaders.mylambert.fragmentShader,
uniforms: THREE.UniformsUtils.merge( [ shaders.mylambert.uniforms, myUniforms ] ),
attributes :attributes,
lights:true,
shading:THREE.FlatShading
} );
material.setValues( parameters );
return material;
};
var myProperties = {
lights: true,
fog: true,
transparent: true
};
var myMaterial = new materials.mylambert( myProperties, uniforms );
Which I got from this Post:
extending lambert material, opacity not working
The vertexShader is basically the shape as shaders.mylambert.vertexShader but with the additional code from the shader example on top.
It works somehow, so the vertices move, but the faces didn't shade when they change their shape so they always have the same shader when I use a plane for example as a the mesh.
In short;
I need a Lambert/Phong Shader that manipulates the Vertices over time up and down to simulate a low Poly Water surface.
If this is still relevant, You can solve this issue much simpler:
Have your model render with a Lambert, Phong, Standard or whatever lit material you like.
Create another Scene, Camera and a WebGLRenderTarget, create a plane and apply your ShaderMaterial to it. Position your camera so that the plane fits exactly your entire frame of the scene.
Render the other Scene to the WebGlRenderTarget and apply it as a map to your original Lambert material this way:
let mat = new THREE.MeshLambertMaterial({
map: renderTarget.texture
})
Viola! You now have a fully lit ShaderMaterial as you like.