THREE.js SphereGeometry Panorama hotspots using DOMElements - javascript

I've created a simple WebGL 3D Panorama application using a SphereGeometry, PerspectiveCamera and a CanvasTexture. Now, I'm looking to bring the scene to life by adding "HotSpots" over certain parts of the SphereGeometry. The problem I'm having is understanding how to update the various DOMElements so that their position is reflective of the updated Camera position.
Basically, as the Camera rotates the various DOMElements would move in and out of view relative to the direction the camera is spinning. I tried positioning a <div> absolute to the <canvas> and translating the X and Y position using the returned PerspectiveCamera camera.rotation but it didn't really work; here's my JS & CSS implentation:
CSS
#TestHotSpot {
/* not sure if using margins is the best method to position hotspot */
margin-top: 50px;
margin-left: 50px;
width: 50px;
height: 50px;
background: red;
position: absolute;
}
JavaScript
/**
CONFIG is my stored object variable; it contains the PerspectiveCamera instance
code is being called inside of my RequestAnimationFrame
**/
config.camera.updateProjectionMatrix();
config.controls.update(delta);
document.getElementById("TestHotSpot").style.transform = "translate("+ config.camera.rotation.x +"px, "+ config.camera.rotation.y +"px)";
Here is also a live example of the desired effect.
What would be the best solution to fix this problem? What I noticed when I ran this that the DOMElements would only slightly move; I also noticed that they wouldn't really take in account where along the SphereGeometry they were placed (for example, being positioned "behind" the Camera; really complex!
I'm happy to use any plugins for the THREE.js engine as well as follow any tutorials. Thank you so much for replying in advance!

Try to set a planemesh/mesh to the desired point.
Copy the position of the css elements (domElements created with three.js cssObject [if you already know]) along with planemesh/mesh 's position.
And you will be done !!

Okay, chiming in on my own question! I had a few problems but I've figured out how to get CSS3d mixing in with THREE.js WebGL scenes.
So the first thing I had to do was update my THREE.js Library; I was running 71 from an earlier download but I needed to update it to the library Mr.Doob was using in this example. I also updated my CSS3d library to the file included in that same example.
I then used this method (the same specified in the link) to create a demo scene.
var camera, scene, renderer;
var scene2, renderer2;
var controls;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.set( 200, 200, 200 );
controls = new THREE.TrackballControls( camera );
scene = new THREE.Scene();
scene2 = new THREE.Scene();
var material = new THREE.MeshBasicMaterial( { color: 0x000000, wireframe: true, wireframeLinewidth: 1, side: THREE.DoubleSide } );
//
for ( var i = 0; i < 10; i ++ ) {
var element = document.createElement( 'div' );
element.style.width = '100px';
element.style.height = '100px';
element.style.opacity = 0.5;
element.style.background = new THREE.Color( Math.random() * 0xffffff ).getStyle();
var object = new THREE.CSS3DObject( element );
object.position.x = Math.random() * 200 - 100;
object.position.y = Math.random() * 200 - 100;
object.position.z = Math.random() * 200 - 100;
object.rotation.x = Math.random();
object.rotation.y = Math.random();
object.rotation.z = Math.random();
object.scale.x = Math.random() + 0.5;
object.scale.y = Math.random() + 0.5;
scene2.add( object );
var geometry = new THREE.PlaneGeometry( 100, 100 );
var mesh = new THREE.Mesh( geometry, material );
mesh.position.copy( object.position );
mesh.rotation.copy( object.rotation );
mesh.scale.copy( object.scale );
scene.add( mesh );
}
//
renderer = new THREE.WebGLRenderer();
renderer.setClearColor( 0xf0f0f0 );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
renderer2 = new THREE.CSS3DRenderer();
renderer2.setSize( window.innerWidth, window.innerHeight );
renderer2.domElement.style.position = 'absolute';
renderer2.domElement.style.top = 0;
document.body.appendChild( renderer2.domElement );
}
function animate() {
requestAnimationFrame( animate );
controls.update();
renderer.render( scene, camera );
renderer2.render( scene2, camera );
}
After I had that working, I re-purposed the CSS3d scene, CSS3d object and the CSS3d renderer to be included in my scene.
Good luck to anyone else and hopefully this helps!

Related

Threejs - Load image with textureloader - problem with planegeometry size and scale 1:1

I'm trying to load images with TextureLoader and I have a big problem. I don't know how I can add images to mesh in scale 1:1 and how I can calculate PlaneGeometry. I want to display the loaded image in its original size without function scaling. It is very important that the image is displayed in its original size without distortions and blurring.
My code:
function init() {
container = document.getElementById('cad-view');
scene = new THREE.Scene();
scene.background = new THREE.Color( 0xffffff );
height = $('.page-footer').offset().top - $('.frame-wrap').offset().top - $('.frame-wrap').outerHeight();
camera = new THREE.PerspectiveCamera( 70, window.screen.width / height, 1, 5000 );
camera.position.z = 1;
camera.position.x = 100;
camera.position.y = 0;
renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
var loader = new THREE.TextureLoader();
var mapGeometry = new THREE.PlaneGeometry(1.052, 0.6329);
var imageMap = new THREE.MeshBasicMaterial({ map: loader.load('mapa.jpg') });
var objectMap = new THREE.Mesh(mapGeometry, imageMap);
console.log(objectMap);
objectMap.position.set(100,0,0);
scene.add(objectMap);
objects.push( objectMap );
controls = new DragControls( [ ... objects ], camera, renderer.domElement );
controls.addEventListener( 'drag', render );
window.addEventListener( 'resize', onWindowResize );
document.addEventListener( 'click', onClick );
window.addEventListener( 'keydown', onKeyDown );
window.addEventListener( 'keyup', onKeyUp );
render();
}
Thank you a lot for any help :)
If you're stretching a square plane with PlaneGeometry(1.052, 0.6329), then you have two options:
Resize your original texture in image-editing software so it matches that aspect ratio.
Use the Texture.repeat() attribute to make the texture scale. You'll have to get your aspect ratio, then use that. I'm not sure of the math, but I think it's something like:
const texture = loader.load('mapa.jpg');
const ratio = 0.6329 / 1.052;
texture.repeat.set(1, ratio);
var imageMap = new THREE.MeshBasicMaterial({map: texture});
Read more about texture.repeat in the docs.

Three.js ParametricGeometry animation (dynamically changing function)

I am really new to computer graphics, and I started experimenting with some things with THREE.js. So I wanted to an animation of a flag (wave motions) and I couldn't find anything (maybe I don't know what to search). So I made my flag with a parametric geometry, and the function is just a cos. And I wan't to animate the flag by dynamically changing the function of the parametric geometry. How can I do that, and is this the correct way of doing this?
P.S. The change in the function that I want is simply moving the cos along the X asis so it looks like the flag is moving.
In dependency on amount of vertices in your mesh, you can use shaders or simple changing of vertices in your animation loop with cos function.
Below, there's the approach with simple changing.
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.set( 0, 2, 10 );
var renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var controls = new THREE.OrbitControls( camera, renderer.domElement );
scene.add( new THREE.GridHelper( 10, 10 ) );
var planeGeom = new THREE.PlaneGeometry( 10, 3, 20, 3 );
var plane = new THREE.Mesh( planeGeom, new THREE.MeshBasicMaterial( { color: "red", side: THREE.DoubleSide } ) );
scene.add( plane );
render();
function render(){
requestAnimationFrame( render );
planeGeom.vertices.forEach( v => {
v.z = Math.cos( .5 * v.x - Date.now() * .001 ) * .5;
});
planeGeom.verticesNeedUpdate = true; // the most important thing, when you change vertices
renderer.render( scene, camera );
}
body{
overflow: hidden;
margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

How to make 3D objects jump smoothly apart from each other upon click? Three.JS

I'm very new to Three.JS and 3D web dev in general what I'm trying to do is mimic this action: https://www.youtube.com/watch?v=HWSTxPc8npk&feature=youtu.be&t=7s Essentially this is a set of 3D planes and upon click the whole stack reacts and gives space around the one that's clicked.
For now, my base case is 3 planes and figuring first out if I can click the the middle one, how do I get the others to jump back smoothly as if they were pushed rather than instant appear and disappear as they do now on the click of a button.
The long term goal is to have a separate button for every plane so that on click, the selected plane will have padding around it and the rest of the planes in stack move accordingly.
I've looked into Tween.js, and CSS3D but pretty overwhelmed as a newbie. Any tutorials or tips would be greatly appreciated!
// Our Javascript will go here.
window.addEventListener( 'resize', onWindowResize, false );
function onWindowResize(){
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var geometry = new THREE.PlaneGeometry( 3, 3, 1 );
var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
var plane = new THREE.Mesh( geometry, material );
plane.rotation.y = -.7;
var material2 = new THREE.MeshBasicMaterial( { color: 0x0000ff } );
var material3 = new THREE.MeshBasicMaterial( { color: 0xff0000 } );
var plane2 = new THREE.Mesh(geometry, material2 );
plane2.rotation.y = -.7;
plane2.position.x = 1;
var plane3 = new THREE.Mesh(geometry, material3);
plane3.rotation.y = -.7;
plane3.position.x = -1;
scene.add( plane, plane2, plane3 );
camera.position.z = 5;
function render() {
requestAnimationFrame( render );
// cube.rotation.x += 0.1;
// cube.rotation.y += 0.1;
renderer.render( scene, camera );
}
render();
function clickFirst() {
TWEEN.removeAll();
var tween = new TWEEN.Tween(plane3.position).to({x: -2}, 1000).start();
tween.easing(TWEEN.Easing.Elastic.InOut);
render();
}
</script>
<button onclick="clickFirst();" style="background-color: white; z-index: 9999;">Click me</button>
First, you need to locate the 2 planes.
Second, you need to make the planes clickable:
https://threejs.org/examples/#webgl_interactive_cubes
https://github.com/josdirksen/learning-threejs/blob/master/chapter-09/02-selecting-objects.html
Third, you should use Tween.js for the transition.
after picking the right plane, make a tween for the other planes with a tween, all to move on the same Axis:
example:
createjs.Tween.get(plane3.position.z).to(
plane3.position.z + 100
, 1000, createjs.Ease.cubicOut)
If you will add some code here after starting to implement i would be able to help more.

Object rotation using the Device Orientation Controls in Three.js

I am making my first steps coding with JavaScript and playing with Three.js.
I am experimenting with this example from threejs.org :http://threejs.org/examples/#misc_controls_deviceorientation
and this is the code that they have:
(function() {
"use strict";
window.addEventListener('load', function() {
var container, camera, scene, renderer, controls, geometry, mesh;
var animate = function(){
window.requestAnimationFrame( animate );
controls.update();
renderer.render(scene, camera);
};
container = document.getElementById( 'container' );
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1100);
controls = new THREE.DeviceOrientationControls( camera );
scene = new THREE.Scene();
var geometry = new THREE.SphereGeometry( 500, 16, 8 );
geometry.scale( - 1, 1, 1 );
var material = new THREE.MeshBasicMaterial( {
map: new THREE.TextureLoader().load( 'textures/2294472375_24a3b8ef46_o.jpg' )
} );
var mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
var geometry = new THREE.BoxGeometry( 100, 100, 100, 4, 4, 4 );
var material = new THREE.MeshBasicMaterial( { color: 0xff00ff, side: THREE.BackSide, wireframe: true } );
var mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.domElement.style.position = 'absolute';
renderer.domElement.style.top = 0;
container.appendChild(renderer.domElement);
window.addEventListener('resize', function() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}, false);
animate();
}, false);
})();
I am trying to control an object that I made with the orientation of a mobile device. I can do it, is just change this line
controls = new THREE.DeviceOrientationControls( camera );
for this line: controls = new THREE.DeviceOrientationControls( object );
But now, my problem is that It changes the initial rotation of the object:
It should be like this:
And I see this in my desktop:
And this in a mobile device:
I tryied to change the DeviceOrientationControls file but is not the best way I think.
Then I found this in Stack Overflow Orbiting around the origin using a device's orientation and they said that is not possible to do it with control device orientation, its necessary to modify the Orbit Controls, and it is very complicated too.
So my question is: Is there a simple way to change the initial rotation of an object and to limit it too? Using DeviceOrientationControls.js
EDIT
I found a way to make it without using the device orientation controls. I used this:
window.addEventListener('deviceorientation', function(e) {
var gammaRotation = e.gamma ? e.gamma * (Math.PI / 600) : 0;
monogram.rotation.y = gammaRotation;
});
It works perfectly when I use my device in a vertical position,but when I use it in landscape position it doesn't work. Do you have a suggestion?

Three.js - troubles with the size of texture

Less words, more code =)
var objects = [];
var camera, scene, renderer;
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
init();
render();
function onDocumentMouseDown( event ) {
event.preventDefault();
var vector = new THREE.Vector3( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1, 0.5 );
projector.unprojectVector( vector, camera );
var ray = new THREE.Ray( camera.position, vector.subSelf( camera.position ).normalize() );
var intersects = ray.intersectObjects( objects );
if ( intersects.length > 0 ) {
console.log(intersects[ 0 ].object);
}
}
function init() {
container = document.getElementById( 'container' );
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 90, window.innerWidth / window.innerHeight, 1, 1100 );
camera.position.z = 50;
scene.add( camera );
var particle = new THREE.Particle( new THREE.ParticleBasicMaterial( { map: THREE.ImageUtils.loadTexture( "img/satellite.png" ) } ) );
objects.push( particle );
//particle.scale.x = particle.scale.y = 0.25
scene.add( particle );
projector = new THREE.Projector();
renderer = new THREE.CanvasRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
}
function render() {
camera.lookAt( scene.position );
renderer.render( scene, camera );
}
As a result, we get clickable particle with a texture. But I don't understand several things:
Why the "clickable" area of particle is so small? It works only if I click in the middle of a particle.
Why is that particle so huge? The texture is this .png file and the particle is way more bigger than 16×16. How can I fix that? Yes, I know about particle.scale, that will make particle look smaller. But, the "clickable" area of particle woukd also become smaller.
I know this is an old question but I came across the same issue today and i found this question unanswered, after some workaround i came across a solution for this.
The solution is to create 2 particles, one as a simple particle that draws a geometry (rect or arc) that is a ParticleCanvasMaterial and then the particle that displays the image on top of it.
So you can use the ParticleCanvasMaterial to track the intersections and display the other particle as a dummy object where it's only purpose is displaying an image on the 3D scene.
A little bit of code:
var programFill = function (context) {
context.beginPath();
context.rect(-0.5, -0.38, 1, 1);
//context.fill();
}
//creating particle to intersect with.
var p = new THREE.ParticleCanvasMaterial({ program: programFill, transparent: true });
var particle = new THREE.Particle(p);
particle.scale.set(23, 23);
//use same position for both particle and imgParticle
particle.position.set(200, 300, 200);
//creating particle that displays image.
var imgTexture = THREE.ImageUtils.loadTexture('images/image.png');
var p2 = new THREE.ParticleBasicMaterial({
map: imgTexture
, size: 1
});
var imgParticle = new THREE.Particle(p2);
imgParticle.scale.x = 0.5;
imgParticle.scale.y = 0.5;
imgParticle.position.set(200, 300, 200);

Categories

Resources