Issue while using dat.GUI in a three.js example - javascript

I tried to use dat.GUI in the following three.js example.
I just did the following code changes to add a GUI to adjust mesh opacity.
var loader=new THREE.VTKLoader();
loader.load ("models/vtk/bunny.vtk", function(geom){
var mesh = new THREE.Mesh(geom, material );
mesh.doubleSided=true;
mesh.position.setY(-0.09);
scene.add( mesh );
var gui = new dat.GUI();
var view = this;
view.Opacity = 0.2;
var maingui = gui.addFolder('Main');
var opacity = maingui.add(view, 'Opacity', 0, 1);
opacity.onChange( function(value) {
mesh.material.opacity = value;
});
maingui.open();
animate();
Now, once I click the opacity slider the mouse is just following the slider. I am not able to come out of the mouse click.

Move the controls init block after the renderer init block, and change this line:
controls = new THREE.TrackballControls( camera );
to this:
controls = new THREE.TrackballControls( camera, renderer.domElement );

Old subject but I just have a similar issue and previous solution wasn't ok for my case. To fix it I create a specific canvas for the dat.gui module:
Html part:
<div id="my-gui-container"></div>
Css part:
#my-gui-container{
position: absolute;
top: 0;
right: 0;
z-index: 100;
}
Js Part:
this.gui = new dat.GUI({ autoPlace: false });
var customContainer = document.getElementById('my-gui-container');
customContainer.appendChild(this.gui.domElement);
With this method the elements are isolate and I have no event conflict anymore.
Edit:
for more details you have all code here https://github.com/quentinchap/threeJs-morphing-test

var clock = new THREE.Clock();
var trackballControls;
function render() {
stats.update();
var delta = clock.getDelta();
trackballControls.update(delta);
// render using requestAnimationFrame
requestAnimationFrame(render);
webGLRenderer.render(scene, camera);
}
function trackBall(){
trackballControls = new THREE.TrackballControls(camera, render.domElement) ;
trackballControls.rotateSpeed = 1.0;
trackballControls.zoomSpeed = 1.0;
trackballControls.panSpeed = 1.0;
// trackballControls.noZoom=false;
//trackballControls.noPan=false;
trackballControls.staticMoving = true;
//trackballControls.dynamicDampingFactor=0.3;
}
trackBall();
render();
I changed my code as shown in the example to
"THREE.TrackballControls(camera, renderer.domElement)"
but I still effect my GUI when I used it once and can't deselect it from this moment. Is there any possibility to deactivate or to deselect the active GUI parts with a command? or something like "gui.deselect.all" ?

Related

How to create hotspots in 3d enviroment using threejs?

I'm a beginner to three.js.I'm trying to build something similar to this https://virtualshowroom.nissan.in/car-selected.html?selectedCar=ext360_deep_blue_pearl. I built everything using three.js, but I'm not able to figure out how to create a hotspot(like the red dot in the above link) and show pop up when you click on it. below is my project code, let me know if anything else is required.
<html>
<head>
<title>My first three.js app</title>
<style>
body { margin: 0; }
canvas { display: block; }
</style>
</head>
<body>
<h1></h1>
<script src="./three.js"></script>
<script type="module">
import { GLTFLoader } from 'https://threejs.org/examples/jsm/loaders/GLTFLoader.js';
import { OrbitControls } from 'https://threejs.org/examples/jsm/controls/OrbitControls.js';
var renderer,scene,camera;
scene = new THREE.Scene();
scene.background = new THREE.Color(0xfff6e6)
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var loader = new GLTFLoader();
var hlight = new THREE.AmbientLight(0x404040, 100)
scene.add(hlight)
var directionalLight = new THREE.DirectionalLight(0xffffff, 100)
directionalLight.position.set(0,1,0)
directionalLight.castShadow = true
scene.add(directionalLight)
var light = new THREE.PointLight(0xffffff, 10)
light.position.set(0, 300, 500)
scene.add(light)
var light2 = new THREE.PointLight(0xffffff, 10)
light2.position.set(500, 100, 0)
scene.add(light2)
var light3 = new THREE.PointLight(0xffffff, 10)
light3.position.set(0, 100, -500)
scene.add(light3)
var light4 = new THREE.PointLight(0xffffff, 10)
light4.position.set(-5000, 300, 0)
scene.add(light4)
var controls = new OrbitControls(camera, renderer.domElement);
document.body.appendChild(renderer.domElement)
var loader = new GLTFLoader();
loader.load( './scene.gltf', function ( gltf )
{
scene.add( gltf.scene );
}, undefined, function ( error ) { console.error( error ); } );
// load a image resource
camera.position.z = 5;
var animate = function () {
requestAnimationFrame( animate );
renderer.render( scene, camera );
};
animate();
</script>
</body>
</html>
Those “hotspots” as you call them are Annotations where the annotation content is basically pure HTML.
The tutorial in the link is probably the best step-by-step readiness you can follow to learn how to do it in your scene.
I can give a walkthrough on the steps required to get the desired effect since I have done it a few times myself.
define a 3d point in your scene where the hotspot should be. You can optionally nest this in a an other Object3D to make sure it scales, moves and rotates with the model / parent.
Add a plane to this point load a image texture to this plane. and there you have your visible hotspot
update the hotspots to make sure they are always looking at the camera by using the lookAt function.
when the user clicks the screen cast a raycast against all the hotspots you have in your scene. Easiest way to do this is by storing all your hotspots in an array.
When the raycast hits a hotspot get the location either of the hitpoint or the hotspots location. Transform that to screen coordinates. Search on stackoverflow how to do this. I am sure there is a post about this.
Final step display your html on the correct location you obtained from the previous step.
The advantage of this method is that the hotspot will integrate nicely with the model in your scene. Since html based hotspots will always be on top of the scene.
That is about all that is to it. Let me know if you need any further clarification!

Changing sites as continous animation with Three.js

I was looking at this website called Garden-Eight and they have this very beautiful transition when you change from "Home" to "About Us". I am really interested in how they made it so smooth and continuous without loading screens or any loading time at all.
It looks like it has to be one scene, but watching the URL one can watch a change in the address so there must be some kind of change in the site location.
I am not necessarily looking at this kind of highly complex transitions.
It would be great if I could create some kind of seemingly continuous camera movement along the x axis that changes as a starting point.
For example, I made this very short animation with this JavaScript:
var camera, scene, renderer, geometry, material, mesh, geometryNew, materialNew, meshNew;
init();
animate();
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.z = 500;
scene.add(camera);
geometry = new THREE.CubeGeometry(200, 200, 200);
material = new THREE.MeshNormalMaterial();
mesh = new THREE.Mesh(geometry, material);
mesh.position.set(0,0,0);
scene.add(mesh);
geometryNew = new THREE.CubeGeometry(200, 200, 200);
materialNew = new THREE.MeshNormalMaterial();
meshNew = new THREE.Mesh( geometryNew, materialNew);
meshNew.position.set(800,0,0);
scene.add(meshNew);
renderer = new THREE.CanvasRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.02;
meshNew.rotation.x -= 0.01;
meshNew.rotation.y -= 0.02;
if(camera.position.x < 800)
camera.position.x += 3
renderer.render(scene, camera);
}
You can watch the animation in this JSFiddle.
What I want to achieve is that the first cube is on the landing page and the second cube is on another page. Can this be done by some kind of asynchronous loading or is there a built-in method, that helps to switch from one canvas to the other?
This is a single-page-app, (SPA), which means it's all one HTML page, regardless what the URL updates to. Some frameworks, like React, have a very complex methodology to implement this, and it would take an entire tutorial to teach.
For simplicity's sake, I recommend you look into the window.onhashchange event to do a fake "change address" without loading a new page. For instance, going from site.com/ to site.com/#about would trigger the event, and then you can start your animation on the same canvas:
window.onhashchange = function() {
if (location.hash === '#about') {
// animate camera to about section
} else if (location.hash === '#work') {
// animate camera to work section
} else if (location.hash === '') {
// animate camera to home section
}
}
Then you can set the hash to change the URL and trigger the event above:
// Adds #about hash to URL, and triggers event listener
location.hash = "#about";
// Removes any hash, and triggers event listener
location.hash = "";

Adding Three.JS 'OrbitControls' Causes Black-Screen in VueJS application?

I have been working through the basic cube exercise on Three.js, integrating the three.js cube into my Vue.JS application.
All was well, my cube rotated as planned using animate etc.
However, it all falls apart when I add OrbitControls.
I have tried three different versions of npm package: three-orbitcontrols, three-orbit-controls(THREE) and now orbit-controls-es6.
Regardless of which one I use, as soon as I remove comments in front of:
controls = new OrbitControls(camera, renderer.domElement) my screen canvas goes black...
I have included my code below so you can see what I have.
I've spent hours testing and searching today, but to no avail.
Hope someone can shine some light on my error.
Thanks all!
<template>
<div id="container"></div>
</template>
<script>
import * as Three from "three";
import OrbitControls from 'orbit-controls-es6';
export default {
name: 'ThreeWindow',
data() {
return {
camera: null,
scene: null,
renderer: null,
mesh: null
}
},
methods: {
init() {
let container = document.getElementById('container');
this.camera = new Three.PerspectiveCamera(70, container.clientWidth/container.clientHeight, 0.01, 10);
this.camera.position.z = 1;
this.scene = new Three.Scene();
let geometry = new Three.BoxGeometry(0.2, 0.2, 0.2);
let material = new Three.MeshNormalMaterial();
this.mesh = new Three.Mesh(geometry, material);
this.scene.add(this.mesh);
//var controls = new OrbitControls(camera, renderer.domElement);
//This line here breaks the code and sends canvas black with no cube.
this.renderer = new Three.WebGLRenderer({antialias: true});
this.renderer.setSize(container.clientWidth, container.clientHeight);
container.appendChild(this.renderer.domElement);
},
animate() {
requestAnimationFrame(this.animate);
//controls.update();
this.mesh.rotation.x += 0.01;
this.mesh.rotation.y += 0.02;
this.renderer.render(this.scene, this.camera);
}
},
mounted() {
this.init();
this.animate();
}
}
</script>
<style scoped>
#container {
width: 1000px;
height: 1000px;
}
</style>
[Cube Image Provided.][1]
[1]: https://i.stack.imgur.com/UwEEi.png
Agree with the previous answer, import orbit controls as follows - worked for me:
import * as Three from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
But you have some other errors that are breaking it:
As you are working with a VueComponent object, you need to modify your camera and renderer variables that you pass in to the OrbitControls constructor with the this reference, so they are referring to the variables attached to your component,
So you need to replace
var controls = new OrbitControls(camera, renderer.domElement);
with
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
and move that line below the line where you declare the this.renderer
so you end up with:
this.renderer = new Three.WebGLRenderer({antialias: true});
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
then similarly in your animate() function,
replace controls.update() with this.controls.update()
you should import OrbitControl as below:
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
First, check for errors in the browser console.
The first thing that pops out to me is the second argument you are sending to the OrbitControls constructor is renderer.domElement, but the renderer object is created after this line.
Try moving the OrbitControls call to after the renderer is created.
ie.
this.renderer = new Three.WebGLRenderer({antialias: true});
this.renderer.setSize(container.clientWidth, container.clientHeight);
var controls = new OrbitControls(camera, renderer.domElement); /*<-- moved to after renderer is created*/
I was experimenting with Three.js and Vue a couple of years back, You may find it interesting or helpful

THREEJS Can't clone FBX model

I am loading FBX models for my project. I Want to create multiple objects of the same FBX model but loading each separately is very inefficient so I would like to save one loaded model and clone it. This concept is working with the .OBJ format.
Everything works as expected but then I use "model.clone();" nothing really happens.
The model is no longer visible but I get no errors. If you compare the cloned model with the original they look exactly the same so I don't know why it's not showing up.
This is how it looks when it's working:
When it's not working it's just a blank canvas.
Here is some simplified working source code:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<script src="https://vitalkia.com/jsLibraries/zlib.min.js"></script>
<script src="https://threejs.org/build/three.js"></script>
<script src="https://threejs.org/examples/js/loaders/FBXLoader.js">
</script>
<script>
var aspectRatio = 240 / 135;
var scene;
var renderer;
var loaderFBX;
var camera = new THREE.PerspectiveCamera(45, aspectRatio, 1, 10000);
//Actual code
//Setus up scene and renderer
sceneSetup();
//Load fbx and render
//IGNORE ERROR ABOUT NOT FINDING THE RIGHT TEXTURE. It still works
loaderFBX.load("https://threejs.org/examples/models/fbx/Samba Dancing.fbx", function(model){
//Sending the object "model" works
//But using model.clone() does not. It doesn't give any errors or anything, it's just not visible
//If you compare the cloned model with the original they look exactly the same.
var sendModel = model;//Works
//var sendModel = model.clone();//Doesn't works
console.log("Original: ");
console.log(model);
console.log("Cloned: ");
console.log(model.clone());
calcDistance(sendModel);
scene.add(sendModel);
renderer.render(scene, camera);
});
function calcDistance(model){
var bbox = new THREE.Box3().setFromObject(model);
var camDistance = bbox.max.z;
if(bbox.max.x > camDistance){
camDistance = bbox.max.x;
}
if(bbox.max.y*aspectRatio > camDistance){
camDistance = bbox.max.y*aspectRatio;
}
camera.position.z = camDistance;
model.position.x -= bbox.getCenter().x;
model.position.y -= bbox.getCenter().y;
}
function sceneSetup(){
loaderFBX = new THREE.FBXLoader();
scene = new THREE.Scene();
renderer = new THREE.WebGLRenderer({antialias: true, alpha: true});
renderer.setSize(240, 135);
document.body.appendChild(renderer.domElement);
var light1 = new THREE.PointLight(0xffffff, 1, 100000000);
light1.position.set(0, 0, 15);
scene.add(light1);
var newLightPoint = new THREE.HemisphereLight(0x8be2ff, 0xfff9cf, 0.55);
scene.add(newLightPoint);
camera.position.set(0, 0, 100);
scene.add(camera);
}
</script>
Is this how you should do it to reuse models instead of having to reload them for every object? Or is there a better way?

How to make a menu before my three program starts executing

I´ve started programming using three.js a little time ago, and I would like to know how to make a menu on a page before it loaded the program using three itself.So, for instance I would like before running my game to have two options:"start" and "music control" being desplayed before it loaded itself.
Is this made using three, or do you use html, javascript files?(Sorry, I really don´t know how to even start it).
If possible, I would like some tutorials or even videos about it.
I would like something like this for example:
http://www.dilladimension.com/
http://lights.helloenjoy.com/
You probably do want to use html to display the menu and js to control the instantiation of your scene.
Rather than use the page load event to set up your renderer, etc, put them inside a function, and use js events to begin the function:
function start() {
var renderer = new THREE.WebGLRenderer();
...
// more setup
...
renderer.render(scene, camera);
};
Then activate this method with a button or a link:
<button onclick="start()">
Start Scene
</button>
Here's a test implementation:
function start() {
var renderer = new THREE.WebGLRenderer();
renderer.setSize(800, 600);
document.body.appendChild(renderer.domElement);
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(
35, // Field of view
800 / 600, // Aspect ratio
0.1, // Near plane
10000 // Far plane
);
camera.position.set(-15, 10, 10);
camera.lookAt(scene.position);
var geometry = new THREE.BoxGeometry(5, 5, 5);
var material = new THREE.MeshLambertMaterial({
color: 0xFF0000
});
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
var light = new THREE.PointLight(0xFFFF00);
light.position.set(10, 0, 10);
scene.add(light);
renderer.setClearColor(0xdddddd, 1);
renderer.render(scene, camera);
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r77/three.min.js"></script>
<button onclick="start()">
Start Scene
</button>

Categories