How can I smooth my polygons in a Three.js scene? - javascript

I've this model here in Three.js:
Unsmoothed part of the model
I used a plug-in from the Unity asset store, to export the model into a JSON file, then imported in my three.js application. The problem is that the plugin doesn't export the smoothing groups, so the quality of the model doesn't look so good.
Is there any way to smooth everything with three.js?

You can use the THREE.SubdivisionModifier and use it like this:
var modifier = new THREE.SubdivisionModifier(divisions);
// Apply the modifier to your geometry NOT MESH.
modifier.modify( geometry );
Actually it's not included in Three.js build, so you have to import it.
You can get it here
UPDATE 1
Basically you JSON file gets loaded as an Object3d, which is like a container. It's structured like this:
Object3D
children (arrays containing your meshes (the number can change based on the model in your scene))
Mesh (containing data about your geometry, which is what you need to modify).
So in order to modify the "geometry" you need to access it like so:
modifier.modify( mesh.children[0].children[0].geometry );
You'll need to apply a modifier to every model in your scene, so:
modifier.modify( mesh.children[0].children[0].geometry );
modifier.modify( mesh.children[0].children[1].geometry );
modifier.modify( mesh.children[0].children[2].geometry );
depending on the number of models you have.
It's like you have to open a container and inside you find a smaller container, then another one and so on, until you access geometry data. Hope it is clear enough :)

Related

Three.js skinning issue from OpenAssetImport-converted model

I've been trying to understand the maths behind Three.JS's skinning and I've been stuck there for a while.
My use case is loading "any" 3D model that is passed through OpenAssetImport and saved in a custom model format (mostly built for a custom native render, but it's just a representation of assimp's data structures).
In this model format I track:
Meshes (obviously)
Nodes (as in the 'assimp' definition of nodes)
Bones
For each bone, I track:
The node which it is attached to
The individual 'bind matrix' as defined by assimp
I'm then struggling to re-construct the skeleton in Three.JS. My attempt is this:
Generate all the 'nodes' (as in, 'assimp' nodes) as simple THREE.Object3D
Attach the meshes when required
For each bone
Find the node (THREE.Object3D) it's attached to
Create a new THREE.Bone
Apply assimp's 'bind matrix' to the ThreeJS bone with bone.applyMatrix(assimpBone.bindMatrix)
Set the THREE.Bone as a child of the THREE.Object3D
Create a THREE.Skeleton with the array of THREE.Bone objects
Attach it to the mesh with 'attached' bind mode
However, for some models this works, while for others I have big errors with meshes being de-formed (even if the base mesh is still at least recognizable).
Example mesh loaded with skinning disabled:
Same mesh loaded with skinning enabled:

resizing individual models in a single geometry

I have a 3D model of my home town. I would like to use real time data to change the height of the buildings. In my first try, I loaded the buildings as individual meshes and called scene.add(buildingMesh) during setup.
var threeObjects = []
var buildingMesh = new THREE.Mesh(geometry, material)
threeObjects.push(buildingMesh);
$.each(threeObjects,function(i, buildingMesh)
{
buildingMesh.rotation.x += -3.1415*0.5;
buildingMesh.castShadow = true;
buildingMesh.receiveShadow = true;
scene.add(buildingMesh);
});
Which is too slow as my dataset consists of roughly 10.000 building.
So I took the approach to add all the (geometries of the) meshes to a single geometry and wrap that in a mesh to be added to the scene
singleGeometry.merge(buildingMesh.geometry, buildingMesh.matrix); //in a loop
var faceColorMaterial = new THREE.MeshLambertMaterial( { color: 0xffffff, vertexColors: THREE.VertexColors } );
combinedMesh = new THREE.Mesh(singleGeometry, faceColorMaterial);
scene.add(combinedMesh);
Just to make a proof of concept, I'm trying to change the height of a building when I click it. Alas, this is not working.
By adding a new id field, I can get a reference to the faces and vertices and change the color of the building, but I can not for the life of me, get them to to change height.
In my first version, I would just use something like:
buildingMesh.scale.z=2;
But as I have no meshes anymore, I'm kinda lost.
Can anybody help?
disclaimer: I'm new to Three.js, so my question might be stupid...hope it's not :)
If you combine all of your buildings into a single geometry, you're obliterating everything that makes the buildings distinct from each other. Now you can't tell building A from building B because it's all one big geometry, and geometry at its basic level is literally just arrays of points and polygons with no way of telling any of it apart. So I think it's the wrong approach to merge it all together.
Instead, you should take advantage of three.js's efficient scene graph architecture. You had the right idea at first to just add all the buildings to a single root Object3D ("scene"). That way you get all the efficiencies of the scene graph but can still individually address the buildings.
To make it load more efficiently, instead of creating the scene graph in three.js every time you load the app, you should do it ahead of time in a 3D modeling program. Build the parent/child relationships there, and export it as a single model containing all of the buildings as child nodes. When you import it into three.js, it should retain its structure.
JCD: That was not quite the question I asked.
But anyhow, I found a solution to the problem.
What I did was to merge all the geometries, but in stead of using the standard clone function in geometry.merge() I used a shallow reference, which made it possible for me to use the reference in threeObjects to find the correct building and resize the part of the geometry using Mesh.scale, followed by a geometry.verticesNeedUpdate = true;
For further optimization, I split the model into 5 different geometries and only updated the geometry that contained the building

Associating data in a three.js mesh object - in a procedurally generated city

I'm trying to create a procedurally generated city using three.js, which will primarily consist of buildings. A requirements is, when a building is clicked on (with a mouse) information about that building (e.g. name, location etc.) should be displayed - say, as a text box hovering next to that building.
I was trying to look if the three.js mesh object (that would represent a building) had any "data" property where this info can be stored. It doesn't.
Any idea how this functionality could be achieved? TIA.
The Object3D class has a property userData where you can safely add custom data.
Since the Mesh class is derived from Object3D, it has that property, too.
Use it like so:
mesh.userData.name = "TheName";
three.js r.71
Its still JavaScript so you can add a custom object to the mesh:
var building = new THREE.Mesh( [...] );
building.info = { name: "myHouse", lat: 48.12, lng: 8.54, [...] };
console.log( building.info.name ); //myHouse

three.js - is it possible to change an loaded .obj object faces' texture dynamically?

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.

Three.js load multiple separated objects / JSONLoader

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.

Categories

Resources