THREE.JS Multiple groups containing the same objects - javascript

I'm currently learning THREE.js and trying to make a playable rubik's cube.
I want to be able to rotate a face as a whole instead of moving every single cube one at a time, and I can do so by creating a THREE.Group and adding the cubes in it. The problem is that a single cube is contained in multiple faces, and I can't find a solution. If I create an object, add it to a first group, then add it to a second group, it is removed from the first group.
I'm pretty sure there is a workaround but can't find it as I'm really new to THREE.js and 3D programming (I only followed a basic course https://www.udemy.com/3d-programming-with-javascript-and-the-threejs-3d-library/).
There is my code but I don't think it will be very usefull anyway.
https://pastebin.com/Hq66UvBU
Thanks

Welcome to Stack Overflow. Please remember to edit your question to include your code, because when the pastebin link dies, your question loses important context.
The correct way to add an object to a THREE.Group is through the add function, like you do. But an object added to multiple groups will only ever be a child of the last group to which it was added. This is because add looks to see if the object already has a defined parent, and removes the object from that parent before setting the new parent (r97 code).
Example:
let obj = new THREE.Object3D()
let p1 = new THREE.Group()
let p2 = new THREE.Group()
p1.add(obj) // obj.parent == p1
p2.add(obj) // 1. three.js calls p1.remove(obj) 2. obj.parent == p2
Beyond this reason, and as #Mugen87 mentioned, your cubes need to not only be able to have multiple memberships, but also to be able to enter and leave face groups as their positions change. To me this says you will almost need to transform the cubes individually. You could use a THREE.Group to make it easier to conceptualize, but there would be extra overhead to actually implement it that way.

Maybe a late answer but try to work in another way and group the cubes that need to turn the moment the turn function is activated and then turn the group around, then empty the group.

Related

Fabric js : No group member update while moving

My first question here, after years of finding solutions in other's topics !
First, sorry if my english is not perfect !
I'm using Fabric js in my application and I have to implement a button to group texts or images.
However, I noticed that, when I move the group on the canvas, the group coords are updated but not the group's objects'.
On the canvas, objects are moving well with their group, but in the console, it seems that they always have the same 'top' and 'left' values.
Is it a normal groups behaviour, in which case, I really don't understand, or there is something I am missing ?
It can be noted that I added objects in the group with the addWithUpdate(obj) method.
I really hope someone can help me !
Yes this is normal.
However you can find the position without destroying and rebuilding the group in this way:
given a group that is a fabric.Group and a childObject that is a subclass of fabric.Object you can:
1) create a point with childObject top and left
var point = new fabric.Point(childObject.left, childObject.top);
2) get the group transformMatrix:
var transform = group.calcTransformMatrix();
3) transform the point with this matrix:
var actualCoordinates = fabric.util.transformPoint(point, transform);
and you should be set.

Three Js How to copy Object direction that it's facing

I'm currently making a 3d space fighter game for my school project, but I encounter a problem when I want to spawn a projectile according to my ship front face or direction it's facing.
I do find a way to spawn projectile according direction we are facing using vector in the following example
http://www.isaacsukin.com/news/2012/06/how-build-first-person-shooter-browser-threejs-and-webglhtml5-canvas
but I still does'nt undestand how this vector work. Can someone explain to me ?
I understand it is more an linear algebra question than programming. First you must fully understand, what Vector is, and what isn't.
Often mistake is, that a Vector is some coordinate in 3D space (x,y,z). This is no true and this thinking will not allow understand, how to correctly work with vectors in space and let's you ask questions like this one.
https://www.khanacademy.org/math/linear-algebra/vectors-and-spaces
https://www.khanacademy.org/math/linear-algebra/vectors-and-spaces/vectors/v/vector-introduction-linear-algebra
If you need only to copy object rotation, you can use obj2.rotation.set(obj1.rotation.x,obj1.rotation.y,obj1.rotation.z)
If you want so put something to the space object facing, you can use object as parent:
var my_vect = new THREE.Object3D(); // create vector
obj1.add(my_vect); // add to your object
obj1.rotation.set(1,2,3); // rotate object
my_vect.position.translateX(1); // move vector 1 unit before the object
obj1.localToWorld( my_vect.position); // get global coords from local coords
actually my_vect.position.x,my_vect.position.y,my_vect.position.z are the numbers in your scene before the object respecting object orientation.

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

Simulate an infinite number of objects

On this example we can move inside a field of spheres but into certain limits. I want to be able to move infinitely among them. How can I do that ?
The trick is to reuse the spheres that are behind the camera and put them in front of it. Look at how it is done in this example. Here the programmer knows that the user will continue in the same direction so he removes the trees that come at a certain position.
If you use something like the example you quoted, you cannot know which direction the user will take. And so, you can use the same trick, but have to code it an other way. The most obvious is to check the distances with all the spheres regularly, if the user moves. If one sphere is too far behind the camera, you mirror it so it faces the camera, behind the fog.
'Regularly' can mean two things depending on your real number of spheres in your scene :
If you have a small scene and few spheres you can check those distances in your render loop. Neither cheap nor useful, 60 per seconds, but that can be the first coding step
Then the best way would be to use a web worker : you send the positions of the camera and those of the spheres, you let the worker compute all the stuff in its thread, and send instructions back : 'move those spheres to those positions'. Every seconds is more reasonable in the threejs example, but up to you to decide that depending on your scene.
NOTE : if you have a lot of spheres, or any meshes you use instead, like more than 20-30, having a mesh for each of them will slower performances. With few trees on the examples i linked it is ok, but with more objects and/or a heavier scene,
think about merging them all in a single geometry. You can check which sphere is where by deducing from the vertices indices, or adding an attribute that defines each sphere.
this will also impact the worker delay : it will have more to compute so it will need more time.
NOTE 2 : Note 1 would of course delete the level of details that the example aims to illustrate :) (Unless you also implement your own while checking the distances of the spheres....)
If you want to have an illusion of infinite world then you could:
Break your world space into regions (for example cubes).
Detect which region you are currently in.
Make sure you have objects (spheres) in neighbour regions. If some of regions are empty - fix it.
Clear regions which are not needed anymore.
For this you might want to have some class like this:
Class Region {
bool isEmpty = true;
Vector3 center;
float radius; // or 'range'
Array<Sphere> = null; // storage of your objects
// constructors / destructor
generateObjects(params); // perlin noise might be helpful there
removeObjects();
}
and do something like this periodically:
void updateRegions() {
computeClosestGridCoord(myPosition); // which is center of your current region
lookForNeighbourRegions(regionsArray); // and add new Region if needed
deleteOldRegionsStuff(regionsArray);
}

Creating a room with Three.js and cannon.js

I'm just getting started with Three.js and cannon.js and I've been trying to create a simple room for a while with no success. I'm working off of this example and I've been trying to add walls and a ceiling. What is the easiest way to do this? Right now I have
// wall?
wallGeometry = new THREE.PlaneGeometry( 300, 300 );
wallGeometry.applyMatrix( new THREE.Matrix4().makeRotationX( Math.PI));
wallMesh = new THREE.Mesh( wallGeometry, material );
wallMesh.castShadow = false;
wallMesh.receiveShadow = true;
scene.add(wallMesh);
But it's light up weird and I don't bump into it.... And if I try to add it through cannon.js I get an invisible wall but can't see it. Can anyone point me to the right direction?
THREE.js itself has no physics included so when you create any object in it, it will never make you 'bump' into them by itself. You have to code such features yourself or use another library for that (as you are already trying to do with cannon.js).
Next - per default, THREE js will only create one ambient light for your scene. If you want dynamic lightning and shadows, all lights must be provided by you and objects you want to react to lights must be using MeshLambertMaterial or MeshPhongMaterial, for shadows you will have to set couple of properies and use specific types of lights.
In your example, you don't seem to define wall material anywhere - this might be why walls are invisible.
check these and steal pieces of code you need (and it seems like you will need a lot :-)
http://threejs.org/examples/
http://stemkoski.github.io/Three.js/index.html

Categories

Resources