I did a simple 3d model in blender and exported it to .obj file. Now I am loading it using three.js and I want objects that have string 'clickable' in its name to move on Y axis on click.
You can look at it here: http://three.parkz.cz/shop.html
The problem is that the informations parsed from names of objects (which I set in blender) are not corresponding to the right objects.
Loading and parsing:
var loader = new THREE.OBJMTLLoader();
loader.addEventListener('load', function(event) {
object = event.content;
object.name = 'CustomObjects';
for(var i = 0; i < object.children.length; i++) {
//console.log(object.children[i]);
var properties = object.children[i].name.split('_');
if(properties[1] == 'clickable') {
object.children[i].clickable = true;
} else object.children[i].clickable = false;
}
object.rotation.x = 0.5;
object.rotation.y = 0.5;
scene.add(object);
});
loader.load('shop.obj', 'shop.mtl');
For example that two cubes in the center (called '005_kiosek' and '010_kiosek2') are not supposed to be clickable but they are! You can open console and after click on it you can see that they are named wrongly '004_clickable' and '009_clickable'!
Here is my blender file: http://three.parkz.cz/shop.blend
Is it problem on javascript side or blender exports it wrong?
Thank you in advance!
Martin
P.S: Does anybody know why aren't that simple meshes (green and blue) rendered well?
The green and blue mesh are not rendered correctly because, under the current implementation, the obj loader only accepts triangles as faces. As those meshes use polygons, they are not rendered correctly.
As for the names I would try to verify that what I put in the blender file, comes out correct. So when you load the model, just print the name and its clickable attribute, to see if all is correct instead for taking it for granted.
Related
I've got grid of cylinder meshes created simply by
var tile = BABYLON.MeshBuilder.CreateCylinder("tile-" + i, { tessellation: 6, height: 0.1 }, scene);
then I have following event callback
window.addEventListener("click", function (evt) {
// try to pick an object
var pickResult = scene.pick(evt.clientX, evt.clientY);
if (pickResult.pickedMesh != null){
alert(pickResult.pickedMesh.name)
});
Then mouse-click on one of tiles raises message box with correct tile name.
When I add some new meshes (3D model inside .babylon file) by
var house;
BABYLON.SceneLoader.ImportMesh("", "../Content/"
, "house.babylon"
, scene
, function (newMeshes)
{ house = newMeshes[0]; });
For better imagination it's texture of house created from four different meshes which is placed over grid of cylinder tiles.
It's displayed fine but when mouse-click it too much often behave as it would totally ignore there is such a mesh and so pickResult.pickedMesh is either null or pickResult.pickedMesh.name points to tile underlaying my imported mesh in point I've clicked.
Just approximately 5% of mesh area corresponds properly to mouse-clicks (let's say in middle of roof, in middle of walls).
I've tried playing with setting some virtual (hidden) house.parent mesh for that which would not be created by importing meshes but seems as dead end.
Are you aware about some way how enforce that scene.pick(evt.clientX, evt.clientY); would respect mesh hierarchy and would consider all visible parts of overlaying texture?
Just for completeness I'm working with middle part of this 3D model (removed left and right house from that).
EDIT: Demo on BabylonJS playground
you could try change
var pickResult = scene.pick(evt.clientX, evt.clientY);
to
var pickResult = scene.pick(scene.pointerX, scene.pointerY);
as evt corresponds to whole page.
I have model (exported from Blender) which contains two meshes. One mesh is rigged and animated character, second mesh - hair model. I need to select one of the meshes and hide it or change it's texture.
I load model like this:
jsonLoader.load('./models/character.js', function(geometry, materials) {
for(var i = 0; i < geometry.animations.length; i++){
THREE.AnimationHandler.add( geometry.animations[i] );
}
var character = new THREE.SkinnedMesh( geometry );
});
Now both meshes works like one solid mesh.
You can make an object invisible by setting its visible property to false.
character.visible = false;
You need to convert this two meshes in two different objects to make them selectable (see this nice post to learn how to select an object).
Once the object is selected you can change its property to make it visible or hide it.
I am creating a program which populate a world with blocks on the ground upon mouse click (the coordinate is based on the mouse click coordinate, which I've calculated). When creating a new block, it should not intersects with the ones already made beforehand. I achieved this checking with THREE.Raycaster. Basically I cast rays from the block's center in all direction of its vertices.
var ln = preview.geometry.vertices.length;
//for each vertices we throw a ray
for(var i = 0; i < ln; i++){
var pr_vertex = preview.geometry.vertices[i].clone();
var gl_vertex = pr_vertex.applyMatrix4(preview.matrix);
var dr_vector = gl_vertex.sub(preview.position);
var ray = new THREE.Raycaster(preview.position, dr_vector.clone().normalize());
var intersects = ray.intersectObjects(objmanager.getAllObject(), true);
//if there is intersection
if(intersects.length > 0 && intersects[0].distance < dr_vector.length()){
//hide preview material
preview.material.opacity = 0;
//exit checking
break;
}
}
preview is the name of the object. This code already worked perfectly. The problem is, Raycaster.intersectObjects only worked when the ray intersects with the front face of the tested object. I've tested it when I ran the code, if the ray start from outside the tested object, they intersected just fine. But when the ray started if from the inside of the tested block, it didn't catch the intersection with the tested block's surface. I presume that this happens because intersectObjects only worked with the tested object's front surface. Is this true?
Intuitively, I tried to solve this problem using THREE.DoubleSide (making a double-sided objects). But it produced an error because the shader failed to initialize.
Could not initialise shader
VALIDATE_STATUS: false, gl error [0] three.js:25254
buildProgram three.js:25254
initMaterial three.js:23760
setProgram three.js:23830
renderBuffer three.js:22371
renderObjects three.js:23061
render three.js:22935
animLoop
I'm using THREE.MeshLambertMaterial. When I used MeshBasicMaterial, the Raycaster.intersectObjects just didn't work at all.
So I hit a roadblock now. Is there any idea to solve this problem? Either that, or I would have to test the collision manually with each objects' individual faces (which I'm afraid would hit the performance hard).
Thank you in advance.
===============
edit: I forgot to mention, I'm using the newest three.js version, which should be around r63
If you set:
object.material.side = THREE.DoubleSided
you should get hits on both surfaces.
If double sided you'll have two intersections :
object.material.side = THREE.DoubleSide
Currently I have a task that would like to show an .obj object on the web
,and the texture of the .obj file should varies through different user input (clicking several
buttons), so I look into three.js.
After finding for solutions in stackoverflow some time, I found that there are some solutions about changing the object texture in a whole, but cannot find out solutions for particularly of a single face (For example, if the obj is a cube, then just change the texture of the front face). I would need to do that since the texture of different faces in the obj is different
Is is possible to change an loaded .obj object's texture for one face only dynamically? If yes, what would be the trick?
===========================
Updated:
Now my .obj object is a cubeman like the game Minecraft player character (reference image if you do not know what it is: http://static1.wikia.nocookie.net/cb20130604003021/p/protagonist/images/5/52/Minecraft-steve_12.png)
I have import the .obj file from the OBJLoader that comes with the three.js example:
var loader = new THREE.OBJLoader();
loader.load( 'test.obj', function ( object ) {
object.position.x = 0;
object.position.y = 0;
scene.add( object );
} );
I would like to the know the way that can change different faces' texture of the object, as like to change the cloth / face skin of the character. The texture are required to be changed independently since the texture of cloth and face can be change independently.
is it possible to load a scene (e.g. two different cubes) exported from blender to json and identify them?
I need to distinguish between them e.g. to make one rotating and the other moving.
Thank you in advance!
Denv
edit+++
Thank you for your answer!
So if I load two cubes in one JSON file:
loader.load("untitled1.js", function(geometry, materials) {
mesh = new THREE.Mesh( geometry, new THREE.MeshFaceMaterial(materials));
mesh.scale.set( 10, 10, 10 );
mesh.position.y = 0;
mesh.position.x = 0;
scene.add( mesh );
});
How can I move first cube?
mesh.getObjectById(0).position.x = 15;
Doesn't seems to work.
Thank you!
Yes, it is possible to load an entire scene from a json file exported from Blender!
I achieved that with the following process: (Using three.js r80)
First you have to name your different objects in Blender like in the following image Outliner.
Then you can export the file using Three.js json exporter add-on for Blender, but taking care of marking the Scene checkbox like in the following:
Using this option your json file now contains all the meshes on the Blender's Outliner, as you can verify using any text editor. It doesn't matter if the meshes were selected or not.
It is important to know that the root (or parent) object isn't a Geometry anymore. It is labeled with the Object type by now. To access the children objects (of Mesh type) you can use the getObjectByName method on the root object like in the following code:
jsonloader.load( "obj/Books.json", function ( loadedObj ) {
var surface = loadedObj.getObjectByName("Surface");
var outline = loadedObj.getObjectByName("Outline");
var mask = loadedObj.getObjectByName("Mask");
// Watch the objects properties on console:
console.log(loadedObj);
console.log(surface);
console.log(outline);
console.log(mask);
} );
If we check the browser's console, we can see the proper objects assigned. And from now, you can manipulate the children objects independently (move, rotate, change materials, etc.)
Each object loaded has an associated .id. So you can use the Object3D.getObjectById() to find it and apply transforms on it.