Trackballcontrols or Orbitcontrols? - three.js

First please excuse my bad english writing and please note that I am a Javascript NOOB!
I am currently developing a website for a client where I am presenting different kind of "wood cut" in webgl but I am unable to get the desired look an feel...
My goal is to make the viewer look and react like what we can see at Sketchfab.
http://sketchfab.com/show/9f7e1e088f0943b697e809f224f8c76d
The rotation is limited to a certain angle and the model always stay in the right position... With Trackballcontrols the model rotate all the way and its a mess. I have tried to change the quaternion as follow:
from:
_this.object.up.applyQuaternion( quaternion );
to:
_this.object.up.applyQuaternion( new THREE.Vector4(0,0,0,1) );
but it behave weird when I reach a certain angle, the model become jumpy...
Then I tried with Orbitcontrols but it seems that there is no dynamicDamping because the controls are really "dry". I prefer a smoother effect...
1- So is there a way to use Orbitcontrols and get the dynamicDamping to work?
2- Is it possible to modify Trackballcontrols to get the desired result?
an example of my models are here:
http://www.boissilvac.ca/new/nosproduits/patron19
Thank you for your time

Related

Making clickable areas on a 3D model in Three js

I'm creating a webpage for a new beer brand for gamers.
So far, I've created a spinning beer can in Three js. I found a GLTF model, made a texture, added lighting and reflections - all the simple basics - using Drei and Fiber.
What I would love to achieve right now, is to use the beer can as navigation for the page. So if you mouse over the logo on the face of the can, the logo should change color to indicate you can click it, and if you click it, something happens (let's say a modal opens). You can then spin the can around and click different parts of it (some text, an icon, the barcode, etc) to get different content.
I'm not sure how I would achieve this or approach this. I've had a couple of different thoughts:
Map up clickable areas on the model somehow. This didn't seem possible.
Make all the different clickable parts separate objects in the model. This seems like a ton of work, and I'd have to spend a lot of time learning Blender. The can model is pretty simple (used a free one from sketchfab.com for now).
Map up html-elements onto the can using css3drenderer. But I didn't find a way to bend it onto the can well, and it didn't seem like I could use the model for that.
Use many textures with alphas on top of each other on the can and somehow look for transparency on click for each layer. Didn't find any way to do this.
Any ideas on what techniques to use to achieve this? I've done game dev in Unity, but I'm new to Three js and WebGL, so it's really hard to know where to start searching.
The common approach is to use raycasting to detect interaction with a model.
What you're really looking for is a hyperlink. Although you can't manually apply a hyperlink to three.js, you can use THREE.Raycaster() as a loophole.
Once you determine that the model was clicked on, you just do the following to redirect the user:
window.location.href = 'https://example.com/'
That should do it~
EDIT:
Here is an example, based on my reply to your comment:
if(intersects.length > 0){
if(intersects[0] == model.children[2]){ //Second child
window.location.href = 'https://example.com/'
}
}

making animation with glb model

I’m begginer with aframe and I’ve an animated glb model (from Mixamo then Blender) on this page https://glitch.com/edit/#!/danseuse?path=index.html%3A30%3A7
It works correctly with this script :
<a-entity gltf-model="#danseuse"
id="danseuseHHP"
position="0 -.5 -6"
animation-mixer=" timeScale: .5 "
></a-entity>
But I would like the animation begin when I click on the glb model.
I read the post https://stackoverflow.com/questions/51058224/how-can-i-control-a-gtlf2-mesh-animation-in-aframe-with-onmouseover-event/69876030#69876030 but I dont understand the second part of the answer of this post (sorry I don’t know javascript).
I use the aframe-extra, how do I change the entity, with for exemple :
event-set__start="_event: click; ??????
Thanks for your help.
You've run into a few different problems here...
First up, event-set doesn't work well with component names with '-'s in them.
https://github.com/supermedium/superframe/issues/296
So using event-set is out (apart from this, it would probably work). You need to write a small component like this.
<script>
AFRAME.registerComponent('animate-on-click', {
init: function() {
this.el.addEventListener("click", (e)=> {
this.el.setAttribute("animation-mixer", "timeScale: .5")
});
}
});
</script>
Then you can include this on your object:
position="0 -.5 -6"
animate-on-click
></a-entity>
See this glitch...
https://glitch.com/edit/#!/animate-on-click
Now for the final problem (and the reason I used a different GLTF in my glitch).
Your GLTF appears to be a skinned mesh. I don't know much about raycasting with a skinned mesh (when I learn more I will come back & update this), but I do know it involves some complications.
See e.g. https://github.com/mrdoob/three.js/pull/19178
Using your GLTF model, I wasn't able to get any raycasting to work at all. So I think there is a further issue that's to do with your GLTF model, and not a problem with the code you posted.
Hope that helps a bit.
Update after investigatng the issues with raycasting on this model.
I haven't completely understood what is going on here, but as far as I can tell...
Raycasting is correctly checking against each body part.
The first check is against a bounding sphere - the idea is to check whether more detailed raycast calcs are worth doing. This is done just with the base model, without any of the bone transformations applied.
These bounding sphere checks consistently fail.
As far as I can tell that is because the base model components are tiny, and only get scaled up to proper size in the process of having the bone transforms applied.
Because THREE.js does the bounding sphere checks against the components before applying these bone transforms, there is virtually zero chance of the raycaster detecting any collisions.
Potentially you could make some changes to the model to address these issues, but I suspect it will be far simpler just to put an invisible plane directly in front of the model, and use raycasting against that to trigger the start of the animation.
Here's a glitch showing that solution:
https://glitch.com/edit/#!/danseuse2

How to look to objects using lookAt() with a-frame camera component and look-controls

Goal: I want to create a Web based experience where the user need to see a series of elements on the scene and later, I want to leave the user explore alone.
I have several objects around the scene and I want the camera to look at them one by one. I am using the lookat() method but is not working correctly. I found this example on Threejs:
http://jsfiddle.net/L0rdzbej/135/
But my example is not working like the previous example.
After the answer of #Mugen87 is working but with a little modification:
document.getElementById('cam').sceneEl.camera.lookAt
Access the camera in this way. You can see the example here:
https://glitch.com/~aframe-lookat-cam-not-working
Please click on the button "animate camera".
As mentioned in this thread, you have to remove or disable look-controls if you're overriding camera rotation manually. So you can do:
var cameraEl = document.getElementById('camera');
cameraEl.setAttribute('look-controls', {enabled: false});
to disable the controls, perform your lookAt() operations, and then enable the controls via:
cameraEl.setAttribute('look-controls', {enabled: true})
I finally could make it worked. I am new in Threejs and aframe and surely I don't understand rotation, positions,world coordinates good enough but I think I did a decent work. Here the link:
https://glitch.com/~aframe-lookat-camera-working
I hope will be useful for somebody on the future.

Three.js within web worker: Simulating animation without rendering to canvas

I have a hypothetical question:
Is it possible to simulate an animation of objects without rendering it to the canvas. I just want to capture objects' position using Vector.project(camera) and present it using CSS. And THREE.DeviceOrientationControls controls how the camera "view" the simulation.
I tried commenting THREE.WebGLRenderer, but it seems that THREE.PerpectiveCamera cannot update it's MatrixWorld property. Hence, the camera seems to not move and the Vector.project(camera) gives a static value. I do this because I need to put my three.js codes within a web worker.
Do I need still need to use THREE.WebGLRenderer to have a working simulation?
UPDATE:
I checked the following:
I digged deeper into ((three.scene.getObjectByName("one")).matrixWorld.getPosition()).project(three.camera);, I inspect the following values, having the above requirement (inside web worker, no renderer), using this example:
matrix: {"elements":{"0":3.2167603969573975,"1":0,"2":0,"3":0,"4":0,"5":2.1445069313049316,"6":0,"7":0,"8":0,"9":0,"10":-1.000100016593933,"11":-1,"12":5.4684929847717285,"13":2.1445069313049316,"14":-0.2000100016593933,"15":0}}
camera.projectionMatrix: {"elements":{"0":3.2167603969573975,"1":0,"2":0,"3":0,"4":0,"5":2.1445069313049316,"6":0,"7":0,"8":0,"9":0,"10":-1.000100016593933,"11":-1,"12":0,"13":0,"14":-0.2000100016593933,"15":0}}
camera.matrixWorld: {"elements":{"0":1,"1":0,"2":0,"3":0,"4":0,"5":1,"6":0,"7":0,"8":0,"9":0,"10":1,"11":0,"12":-1.7000000476837158,"13":-1,"14":0,"15":1}}
matrix.getInverse(camera.matrixWorld): {"elements":{"0":1,"1":0,"2":0,"3":0,"4":0,"5":1,"6":0,"7":0,"8":0,"9":0,"10":1,"11":0,"12":1.7000000476837158,"13":1,"14":0,"15":1}}
matrix.multiplyMatrices(camera.projectionMatrix, matrix.getInverse(camera.matrixWorld)): {"elements":{"0":3.2167603969573975,"1":0,"2":0,"3":0,"4":0,"5":2.1445069313049316,"6":0,"7":0,"8":0,"9":0,"10":-1.000100016593933,"11":-1,"12":5.4684929847717285,"13":2.1445069313049316,"14":-0.2000100016593933,"15":0}}
But, when normal (no modification), I inspect the following:
matrix: {"elements":{"0":3.2167603969573975,"1":0,"2":0,"3":0,"4":0,"5":2.1445069313049316,"6":0,"7":0,"8":0,"9":0,"10":-1.000100016593933,"11":-1,"12":5.4684929847717285,"13":2.1445069313049316,"14":-0.2000100016593933,"15":0}}
camera.projectionMatrix: {"elements":{"0":3.2167603969573975,"1":0,"2":0,"3":0,"4":0,"5":2.1445069313049316,"6":0,"7":0,"8":0,"9":0,"10":-1.000100016593933,"11":-1,"12":0,"13":0,"14":-0.2000100016593933,"15":0}}
camera.matrixWorld: {"elements":{"0":1,"1":0,"2":0,"3":0,"4":0,"5":-2.220446049250313e-16,"6":-1,"7":0,"8":0,"9":1,"10":-2.220446049250313e-16,"11":0,"12":-1.7000000476837158,"13":-1,"14":0,"15":1}}
matrix.getInverse(camera.matrixWorld): {"elements":{"0":1,"1":0,"2":0,"3":0,"4":0,"5":-2.220446049250313e-16,"6":1,"7":0,"8":0,"9":-1,"10":-2.220446049250313e-16,"11":0,"12":1.7000000476837158,"13":-2.220446049250313e-16,"14":1,"15":1}}
matrix.multiplyMatrices(camera.projectionMatrix, matrix.getInverse(camera.matrixWorld)): {"elements":{"0":3.2167603969573975,"1":0,"2":0,"3":0,"4":0,"5":-4.761761943205948e-16,"6":-1.000100016593933,"7":-1,"8":0,"9":-2.1445069313049316,"10":2.2206681307011713e-16,"11":2.220446049250313e-16,"12":5.4684929847717285,"13":-4.761761943205948e-16,"14":-1.2001099586486816,"15":-1}}
I noticed that the camera.matrixWorld property has significant difference in both condition. I do not understand what makes the difference.
Apparently, the following lines from THREE.WebGLRenderer.render are still needed to update camera.matrixWorld property:
scene.updateMatrixWorld();
camera.updateMatrixWorld();
camera.matrixWorldInverse.getInverse(vs._3.camera.matrixWorld);

camera.lookAt not called when THREE controls are being used

I am working on a program, that uses THREE.RollControls, when the user goes too far away from the center of the screen, they tend to get lost, so I am working on creating a function that reorients them, facing the center of the scene.
What I had intened to do was simply call the following:
camera.lookAt(scene.position)
However, this has no affect. From what I was reading on different stack overflow questions specifically this:
ThreeJS camera.lookAt() has no effect, is there something I'm doing wrong?
It seems like their solution was to do the camera position change using the controls, rather then changing the camera itself.
I do not believe there is any 'Target' in the Roll Controls, so I don't know how I can reset where the camera is looking at based on a THREE.Vector3() Is there a simple way to do this, or will I basically have to:
So far I have 'attempted' to do the follow:
- Calculate the difference of position of the camera with the position of the scene.
- Normalize this vector
- Subtract it from the direction forward of the camera
- use this vector in controls.forward.add(thisVector)
but this doesn't do at all what I want (probably because I have no idea what I'm doing)
Thank you in advance for your time!
Isaac
The same thing bugged me too about the RollControls but I took a different approach in solving the problem. Since the controls are in the example code (in r55) you can modify the controls, as they are not part of the core library. You can see my modifications at http://www.virtuality.gr/AGG/EaZD-WebGL/js/three.js/examples/js/controls/RollControls.js
I introduced a local variable called mouseLook because I could not use the this.mouseLook. I initialized it to false and I only make it true when there is a button press i.e. when navigating in the scene. That solved my problem.

Resources