I have the following scene visable here: http://gravient.thomashoek.com/drag_example/
In the initial mode the text looks at the mouse. but when you make sertain mouse movements the text seems to flip the direct of looking at the mouse. As far as i figured out this happens when the mouse crosses the middle of the canvas.
A quick in depth explanation of the problem: So when the mouse is at the top of the screen the mesh looks at the bottom of the screen. And this is the same for all directions.
But when you move the mouse again in a certain undefined manner the problem fixes it self.
So my question is: How can i prevent this from happening?
My code to do the lookAt is as following:
var data = {
text : ["THE NEXT","POINT","OF VIEW"],
size : 5,
height : 2,
curveSegments : 12,
line_height : 1.7
};
function generateTextGeometry(mesh) {
var y_start = (0 - data.text.length * data.size / 2 - data.size / 2 - (data.text.length - 1) * data.line_height)/2;
var loader = new THREE.FontLoader();
loader.load( '../data/suisse_2.json', function ( font ) {
for (var i = data.text.length - 1; i >= 0; i--) {
var geometry = new THREE.TextGeometry( data.text[i], {
font: font,
size: data.size,
height: data.height,
curveSegments: data.curveSegments
});
geometry.center();
mesh.children[i].geometry = geometry;
mesh.children[i].position.y = y_start;
if (i < data.text.length) {
y_start += (data.size + data.line_height);
}else{
y_start += data.size;
}
}
});
}
//global vars for al the desired options all are false because we dont know if it will work.
var current_interaction_mode = null;
var webcam = false;
var gyroscope = false;
var followMouse = false;
var dragMouse = true;
var rotation_vals = {
x: -0.51,
y: 0.51,
range: 0.2
}
check_user_hardware();
//check if all the wanted options are available
function check_user_hardware(){
window.addEventListener("devicemotion", function(event){
if(event.rotationRate.alpha || event.rotationRate.beta || event.rotationRate.gamma){
if (!gyroscope) {
gyroscope = true;
current_interaction_mode = 'gyroscope_option';
set_user_ui_elements();
}
}else{
followMouse = true;
current_interaction_mode = 'followMouse_option';
set_user_ui_elements();
window.addEventListener('mousemove', get_user_mouse_pos);
}
calculate_rotation_mesh_pos(event.rotationRate.beta, event.rotationRate.gamma);
});
}
function get_user_mouse_pos(event){
rotation_vals.x = ( event.clientX / window.innerWidth ) * 2 - 1;
rotation_vals.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}
function calculate_rotation_mesh_pos(x_angle, y_angle){
// rotation_vals.x = some calculation that needs to be done here
// rotation_vals.y = some calculation that needs to be done here
}
function set_user_ui_elements(){
if (followMouse) {
$('#followMouse_option').show();
}
if (gyroscope) {
$('#gyroscope_option').show();
}
if (dragMouse) {
$('#dragMouse_option').show();
}
if (current_interaction_mode == 'followMouse_option') $('#followMouse_option').css('color', '#ff0000');
if (current_interaction_mode == 'gyroscope_option') $('#gyroscope_option').css('color', '#ff0000');
}
var scene = new THREE.Scene();
var camera = new THREE.OrthographicCamera( window.innerWidth / - 40, window.innerWidth / 40, window.innerHeight / 40, window.innerHeight / - 40, 0, 2000 );
camera.position.z = 1000;
var renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor( 0x000000, 1 );
document.getElementById('interactive_container').appendChild( renderer.domElement );
//set up interaction parameters
var fakeMouseLoc = new THREE.Vector3();
var orbit = new THREE.OrbitControls( camera, renderer.domElement );
orbit.enableZoom = false;
orbit.enableKeys = false;
orbit.enablePan = false;
orbit.enabled = false;
//generate the text mesh
var mesh = new THREE.Object3D();
for (var i = data.text.length - 1; i >= 0; i--) {
mesh.add( new THREE.Mesh(
new THREE.Geometry(),
new THREE.MeshBasicMaterial({
color: 0xFFFFFF
})
));
}
var options = generateTextGeometry(mesh);
scene.add( mesh );
var render = function(){
if (current_interaction_mode == 'followMouse_option' || current_interaction_mode == 'gyroscope_option') {
fakeMouseLoc.set(rotation_vals.x, rotation_vals.y, rotation_vals.range);
mesh.lookAt(fakeMouseLoc);
}
renderer.render( scene, camera );
requestAnimationFrame( render );
};
render();
window.addEventListener( 'resize', function () {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}, false );
function add_geometry_to_container(container){
container.add( new THREE.Mesh(
new THREE.Geometry(),
new THREE.MeshBasicMaterial({
color: 0xFFFFFF
})
));
}
function clear_geometry_of_container(container, empty){
if (!empty) {
container.children[0].geometry.dispose();
container.children[0].material.dispose();
container.remove(container.children[0]);
}else{
for (var i = container.children.length - 1; i >= 0; i--) {
container.children[i].geometry.dispose();
container.children[i].material.dispose();
container.remove(container.children[i]);
}
}
}
function invert_color(state){
if(state){
console.log("switch to white bg");
renderer.setClearColor( 0xFFFFFF, 1 );
for (var i = data.text.length - 1; i >= 0; i--) {
mesh.children[i].material.color.setHex( 0x000000, 1 );
}
}else{
console.log("switch to black bg");
renderer.setClearColor( 0x000000, 1 );
for (var i = data.text.length - 1; i >= 0; i--) {
mesh.children[i].material.color.setHex( 0xFFFFFF, 1 );
}
}
}
<script src="../supporting_files/general/jquery-3.1.0.min.js"></script>
<script src="../supporting_files/3d_interactive/three.min.js"></script>
<script src="../supporting_files/3d_interactive/OrbitControls.js"></script>
<script src='../supporting_files/3d_interactive/geometry.js'></script>
<script src="../supporting_files/general/check_features.js"></script>
<script src="../supporting_files/main_page_js/desktop_main.js"></script>
<script src="../supporting_files/3d_interactive/main_3d_scene.js"></script>
If you need more info, context or something is unclear pls let me know so i can clarify :)
Related
In the link below there is an animation created with triangles. How can I convert triangular areas into squares? A line runs through the middle when using "PlaneGeometry". When this line is deleted, the triangles turn into squares.
https://codepen.io/UIUXLab/pen/pBRzRZ
I want it like in the picture.
( function( $ ) {
"use strict";
$( function() {
var $window = $( window ),
windowWidth = window.innerWidth,
windowHeight = window.innerHeight,
rendererCanvasID = '3D-background-three-canvas5';
// Generate one plane geometries mesh to scene
//-------------------------------------
var camera,
scene,
material,
group,
lights = [],
renderer,
shaderSprite,
clock = new THREE.Clock();
var geometry, plane, simplex;
var factor = 300,
speed = 0.0005, // terrain size
cycle = 0, //move speed
scale = 30; // smoothness
init();
render();
function init() {
//camera
camera = new THREE.PerspectiveCamera( 60, windowWidth / windowHeight, 1, 10000 );
camera.position.set( 0, 0, 100 );
//Scene
scene = new THREE.Scene();
//HemisphereLight
lights[ 0 ] = new THREE.PointLight( 0xff0000, 1, 0 );
lights[ 1 ] = new THREE.PointLight( 0x0000ff, 1, 0 );
lights[ 2 ] = new THREE.PointLight( 0xffffff, 1, 0 );
lights[ 0 ].position.set( 0, 200, 0 );
lights[ 1 ].position.set( 100, 200, 100 );
lights[ 2 ].position.set( - 100, - 200, - 100 );
scene.add( lights[ 0 ] );
scene.add( lights[ 1 ] );
scene.add( lights[ 2 ] );
//WebGL Renderer
renderer = new THREE.WebGLRenderer( {
canvas : document.getElementById( rendererCanvasID ), //canvas
alpha : true,
antialias: true
} );
renderer.setSize( windowWidth, windowHeight );
// Immediately use the texture for material creation
group = new THREE.Object3D();
group.position.set(0,-300,-1000);
group.rotation.set(29.8,0,0);
geometry = new THREE.PlaneGeometry(4000, 2000, 128, 64);
material = new THREE.MeshLambertMaterial({
color: 0xffffff,
opacity: 1,
blending: THREE.NoBlending,
side: THREE.FrontSide,
transparent: false,
depthTest: false,
wireframe: true
});
plane = new THREE.Mesh(geometry, material);
plane.position.set(0, 0, 0);
simplex = new SimplexNoise();
moveNoise();
group.add(plane);
scene.add(group);
// Fires when the window changes
window.addEventListener( 'resize', onWindowResize, false );
}
function render() {
requestAnimationFrame( render );
var delta = clock.getDelta();
//To set a background color.
renderer.setClearColor( 0x000000 );
//change noise values over time
moveNoise();
//update sprite
cycle -= delta * 0.5;
renderer.render( scene, camera );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function moveNoise() {
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = geometry.vertices[Symbol.iterator](), _step2; ! (_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var vertex = _step2.value;
var xoff = vertex.x / factor;
var yoff = vertex.y / factor + cycle;
var rand = simplex.noise2D(xoff, yoff) * scale;
vertex.z = rand;
}
} catch(err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.
return != null) {
_iterator2.
return ();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
geometry.verticesNeedUpdate = true;
cycle += speed;
}
} );
} ) ( jQuery );
The middle line should disappear.
https://threejs.org/docs/#api/en/geometries/PlaneGeometry
You can change index of a plane geometry, and then use the geometry with LineSegments.
See this forum topic: Wireframe of quads
body{
overflow: hidden;
margin: 0;
}
<script type="module">
import * as THREE from "https://cdn.skypack.dev/three#0.136.0";
import {OrbitControls} from "https://cdn.skypack.dev/three#0.136.0/examples/jsm/controls/OrbitControls";
import { ImprovedNoise } from 'https://cdn.skypack.dev/three#0.136.0/examples/jsm/math/ImprovedNoise.js';
let perlin = new ImprovedNoise()
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000);
camera.position.set(0, 5, 10);
let renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
window.addEventListener("resize", event => {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
});
let controls = new OrbitControls(camera, renderer.domElement);
let g = new THREE.PlaneGeometry(20, 20, 50, 50);
let pos = g.attributes.position;
let uvs = g.attributes.uv;
let uv = new THREE.Vector2();
for(let i = 0; i < pos.count; i++){pos
uv.fromBufferAttribute(uvs, i);
uv.multiplyScalar(5);
let z = perlin.noise(uv.x, uv.y, 0) * 3;
pos.setZ(i, z);
}
g.rotateX(-Math.PI * 0.5);
ToQuads(g); // change the index
let m = new THREE.LineBasicMaterial({color: "magenta"});
let o = new THREE.LineSegments(g, m);
scene.add(o);
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
});
function ToQuads(g) {
let p = g.parameters;
let segmentsX = (g.type == "TorusBufferGeometry" ? p.tubularSegments : p.radialSegments) || p.widthSegments || p.thetaSegments || (p.points.length - 1) || 1;
let segmentsY = (g.type == "TorusBufferGeometry" ? p.radialSegments : p.tubularSegments) || p.heightSegments || p.phiSegments || p.segments || 1;
let indices = [];
for (let i = 0; i < segmentsY + 1; i++) {
let index11 = 0;
let index12 = 0;
for (let j = 0; j < segmentsX; j++) {
index11 = (segmentsX + 1) * i + j;
index12 = index11 + 1;
let index21 = index11;
let index22 = index11 + (segmentsX + 1);
indices.push(index11, index12);
if (index22 < ((segmentsX + 1) * (segmentsY + 1) - 1)) {
indices.push(index21, index22);
}
}
if ((index12 + segmentsX + 1) <= ((segmentsX + 1) * (segmentsY + 1) - 1)) {
indices.push(index12, index12 + segmentsX + 1);
}
}
g.setIndex(indices);
}
</script>
I have a simple three.js graphics, and I tried to use the answers in this and this question to make the created plot zoomable by the mouse wheel. By using the mouse wheel I would like to zoom in to the graphics or to zoom out.
Here is the complete code: pastebin link
However, when turning the mouse wheel nothing happens, and I do not get an error message. Maybe I am missing something?
DEMO
var container, camera, scene, renderer, colors;
var selected = 0;
var selectedObject;
var objects = [];
// DOM element...
container = document.createElement('div');
document.body.appendChild(container);
// Camera...
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.set(0, 0, 150);
// Scene...
scene = new THREE.Scene();
scene.add(camera);
// Renderer...
renderer = new THREE.WebGLRenderer({
clearAlpha: 1
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xffffff, 1);
document.body.appendChild(renderer.domElement);
// Scatter plot...
scatterPlot = new THREE.Object3D();
scene.add(scatterPlot);
// Plot some random points...
circle = new THREE.CircleGeometry(1, 20);
colors = [];
var max = 50;
var min = -50;
for (var i = 0; i < 10; i++) {
var object = new THREE.Mesh( circle.clone(), new THREE.MeshBasicMaterial( { color: new THREE.Color('black'), opacity: 0.5 } ) );
object.position.x = Math.random() * (max - min) + min;
object.position.y = Math.random() * (max - min) + min;
object.position.z = 0;
scene.add( object );
objects.push( object );
}
animate();
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
raycaster = new THREE.Raycaster();
mouse = new THREE.Vector2();
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
document.addEventListener( 'mousewheel', onDocumentMouseWheel, false );
function onDocumentMouseDown( event ) {
event.preventDefault();
mouse.x = ( event.clientX / renderer.domElement.clientWidth ) * 2 - 1;
mouse.y = - ( event.clientY / renderer.domElement.clientHeight ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( objects );
if ( intersects.length > 0 ) {
//intersects[ 0 ].object.material.color.set('red');
//intersects[ 0 ].object.geometry.scale(1.1,1.1,1.1);
if (selected === 0) {
selected = 1;
selectedObject = intersects[ 0 ].object;
selectedObject.material.color.set('red');
console.log(selectedObject.position.x);
} else {
selected = 0;
var geometry = new THREE.Geometry();
geometry.vertices.push(intersects[ 0 ].object.position);
geometry.vertices.push(selectedObject.position);
var line = new THREE.Line(geometry, new THREE.LineBasicMaterial({ color: 0x0000ff }));
scene.add(line);
selectedObject.material.color.set('black');
}
}
}
function onWindowResize() {
camera.left = window.innerWidth / - 2;
camera.right = window.innerWidth / 2;
camera.top = window.innerHeight / 2;
camera.bottom = window.innerHeight / - 2;
camera.aspect = window.innerWidth / window.innerHeight;
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseWheel( event ) {
var fovMAX = 160;
var fovMIN = 1;
camera.fov -= event.wheelDeltaY * 0.05;
camera.fov = Math.max( Math.min( camera.fov, fovMAX ), fovMIN );
camera.projectionMatrix = new THREE.Matrix4().makePerspective(camera.fov, window.innerWidth / window.innerHeight, camera.near, camera.far);
}
body { margin: 0; }
canvas { width: 100%; height: 100% }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/54/three.js"></script>
In your code add event listener
document.addEventListener( 'mousewheel', onDocumentMouseWheel, false );
document.addEventListener( 'mousewheel', (event) => {
camera.position.z +=event.deltaY/500;
});
The mousewheel event is supported only in Webkit browsers.
For cross-browser compaitibility use the wheel event.
I've got a double click function to allow the user to double click on a car model and it displays which objects have been intersected; e.g. wipers, grille, tyres and so on, and this function displays them in a list with the number of items the double click intersected with.
However, I am now trying to get it so that when a certain part of the car is clicked, for example, the tyres, it will display a paragraph with information on them. I can see how this is just a case of checking the name of the intersecting object and then displaying the relevant text if it intersects it, but every time I go to do what I think is right, it just breaks the already existing function to the point where the whole thing won't run.
I'm not exactly a JavaScript or Three.js pro at all, but trying to progress my function further is proving to be rather difficult.
Any suggestions? I've included the entire double click function, however it's when it's checking if there has been intersections near the bottom that is where the alterations need to be.
// On Mouse double click event function
function onDoubleClick(event) {
// Set the mouse down flag to false
mouseDown = false;
// Canvas x (left) and y (top) position
var canvasLeft = 0;
var canvasTop = 0;
// "event.clientX" is the mouse x position. "event.clientY" is the mouse y position
var tempX = event.clientX - canvasLeft;
var tempY = event.clientY - canvasTop;
// Create a normalised vector in 2d space
var vector = new THREE.Vector3((tempX / window.innerWidth) * 2 - 1, - (tempY / innerHeight) * 2 + 1, 0.5);
// Unproject a 2D point into the 3D word
// Use the camera projection matrix to transform the vector to the 3D world space
vector.unproject(camera);
// Send a ray in the direction the user has clicked from the cameras position
var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
// Check if the ray has intersected with any objects and get intersections
var intersects = raycaster.intersectObjects(objects, true);
// Check if intersected with objects
if (intersects.length > 0) {
var tempStr = "Number of items: " + intersects.length + " ";
// List the items that were hit
for(var i=0; i < intersects.length; i++){
if(intersects[i].object.name != ""){
// The mesh name set above
tempStr += " | Name: " + intersects[i].object.name;
} else {
// The names inside the model
tempStr += " | Name: " + intersects[i].object.parent.name;
}
}
//Debug information
document.getElementById("debugInfo").innerHTML = tempStr + ".<br>";
//END
}
}
EDIT:
This is the entire code for the javascript file, as altering elements of the double click function seems to stop the page from loading.
window.onload = init;
// declare variables
var scene,camera,renderer, container;
var controls, guiControls, datGUI;
var grid, color;
var cube, cubeGeometry, cubeMaterial;
var plane, planeGeometry, planeMaterial;
var skyBoxMesh, texture_placeholder;
var spotLight;
var stats;
// Handles the mouse events.
var mouseOverCanvas;
var mouseDown;
// An array of objects that can be clicked on
var objects = [];
//DAE models
var showroom ,carOld, carNew;
var daeObject;
var animations;
var kfAnimations = [];
var kfAnimationsLength = 0;
var lastFrameCurrentTime = [];
var clock = new THREE.Clock();
var mouseOverCanvas, mouseDown;
var objectsClick=[];
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
//creates empty scene
scene = new THREE.Scene();
//camera
camera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, .1, 500);
camera.position.x = 40;
camera.position.y = 40;
camera.position.z = 40;
camera.lookAt(scene.position);
//renderer
renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setClearColor(0xe6f2ff);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMapSoft = true;
container.appendChild( renderer.domElement );
// Add an event to set if the mouse is over our canvas
renderer.domElement.onmouseover=function(e){ mouseOverCanvas = true; }
renderer.domElement.onmousemove=function(e){ mouseOverCanvas = true; }
renderer.domElement.onmouseout=function(e){ mouseOverCanvas = false; }
renderer.domElement.onmousedown=function(e){ mouseDown = true; }
renderer.domElement.onmouseup=function(e){ mouseDown = false; }
// Double Click Event. Set a function called "onDoubleClick"
renderer.domElement.ondblclick=onDoubleClick;
// stats
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
container.appendChild( stats.domElement );
//adds controls
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.addEventListener('change', render);
var ambient = new THREE.AmbientLight( 0xadad85 );
scene.add( ambient );
//---------- creates grid ---------------
grid = new THREE.GridHelper(50,5);
color= new THREE.Color("rgb(255,0,0)");
grid.setColors( 0x000000);
scene.add(grid);
//----------- creates cube --------------
cubeGeometry = new THREE.BoxGeometry(5,5,5);
cubeMaterial = new THREE.MeshPhongMaterial({color: 0xff3300});
cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.position.x = 0;
cube.position.y = 6;
cube.position.z = 2.5;
cube.castShadow = true;
scene.add(cube);
//----------- creates plane ---------------
planeGeomenty= new THREE.PlaneGeometry(100,100,100);
planeMaterial = new THREE.MeshLambertMaterial({color: 0x00cc00});
plane = new THREE.Mesh(planeGeomenty, planeMaterial);
//position the add objects to the scene
plane.rotation.x = -.5*Math.PI;
plane.receiveShadow = true;
scene.add(plane);
//------------- skyBox --------------
texture_placeholder = document.createElement('canvas');
texture_placeholder.width = 128;
texture_placeholder.height = 128;
var context = texture_placeholder.getContext('2d');
context.fillStyle = 'rgb(200,200, 200)';
context.fillRect(0, 0,texture_placeholder.width, texture_placeholder.height);
var materials = [
loadTexture('images/skybox/posX.jpg'),
loadTexture('images/skybox/negX.jpg'),
loadTexture('images/skybox/posY.jpg'),
loadTexture('images/skybox/negY.jpg'),
loadTexture('images/skybox/posZ.jpg'),
loadTexture('images/skybox/negZ.jpg')
];
skyBoxMesh = new THREE.Mesh(new THREE.BoxGeometry(500,500,500,7,7,7),
new THREE.MeshFaceMaterial(materials));
skyBoxMesh.scale.x = -1;
scene.add(skyBoxMesh);
//---------- loads collada files -----------
loadCollada();
daeObject = cube;
// initialise datGUI controls values
guiControls = new function() {
this.rotationY = 0.0;
this.positionX = 0.0;
this.positionY = 0.0;
this.positionZ = -10;
this.lightX = 20;
this.lightY = 35;
this.lightZ = 40;
this.intensity = 1;
this.distance = 0;
this.angle = 1.570;
this.target = cube;
}
//add spotLight with starting parameters
spotLight = new THREE.SpotLight(0xffffff);
spotLight.castShadow = true;
spotLight.position.set(20,35,40);
spotLight.intensity = guiControls.intensity;
spotLight.distance = guiControls.distance;
spotLight.angle = guiControls.angle;
scene.add(spotLight);
//adds controls on the scene
datGUI = new dat.GUI();
// datGUI.add(guiControls, 'positionZ', 0, 1);
datGUI.add(guiControls, 'positionZ', -10, 25, 0.5). name("Move the car");
datGUI.add(guiControls, 'rotationY', 0, 1).name('Rotate the car');
datGUI.add(guiControls, 'lightX', -60, 180);
datGUI.add(guiControls, 'lightY', 0, 180);
datGUI.add(guiControls, 'lightZ', -60, 180);
datGUI.add(guiControls, 'target',[ 'cube','Modern Mini', 'Classic Mini']).onChange(function() {
if(guiControls.target == 'cube'){
spotLight.target = cube;
daeObject = cube;
}
else if(guiControls.target == 'Classic Mini'){
spotLight.target = carOld;
daeObject = carOld;
}
else if(guiControls.target = 'Modern Mini'){
spotLight.target = carNew;
daeObject = carNew;
}
});
datGUI.add(guiControls, 'intensity', 0.01, 5).onChange(function (value){
spotLight.intensity = value;
});
datGUI.add(guiControls, 'distance', 0, 1000).onChange(function (value){
spotLight.distance = value;
});
datGUI.add(guiControls, 'angle', 0.001, 1.570).onChange(function (value){
spotLight.angle = value;
});
datGUI.close();
container.appendChild(renderer.domElement);
window.addEventListener( 'resize', onWindowResize, false );
}
//------------------------- END INIT() ----------------------------
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function loadCollada() {
//--- Loads the Classic Mini ---
colladaLoader = new THREE.ColladaLoader();
colladaLoader.options.convertUpAxis = true;
colladaLoader.load( 'dae_files/ClassicMini.dae', function ( collada ) {
carOld = collada.scene; // stores dae file to a global variable
carOld.position.set( 14.5, 1.8, -10 );
carOld.scale.set( 0.04, 0.04, 0.04 );
carOld.traverse(function (child) {
child.castShadow = true;
child.receiveShadow = true;
});
carOld.updateMatrix();
carOld.name = "Classic";
scene.add( carOld );
objects.push( carOld );
} );
//--- loads Modern Mini ---
colladaLoader = new THREE.ColladaLoader();
colladaLoader.options.convertUpAxis = true;
colladaLoader.load( 'dae_files/ModernMini.dae', function ( collada ) {
carNew = collada.scene;
carNew.position.set( -14.5, 6.3, -10 );
carNew.scale.set( 0.06, 0.06, 0.06 );
// creates shadow
carNew.traverse(function (child) {
child.castShadow = true;
child.receiveShadow = true;
});
carNew.updateMatrix();
carNew.name = "Modern";
scene.add( carNew );
objects.push( carNew );
} );
//--- loads the Showroom ---
colladaLoader = new THREE.ColladaLoader();
colladaLoader.options.convertUpAxis = true;
colladaLoader.load( 'dae_files/roomAnim2.dae', function collada( collada ) {
showroom = collada.scene;
animations = collada.animations;
kfAnimationsLength = animations.length;
// Initialise last frame current time.
for ( var i = 0; i < kfAnimationsLength; i++ ) {
lastFrameCurrentTime[i] = 0;
}
// Get all the key frame animations.
for ( var i = 0; i < kfAnimationsLength; i++ ) {
var anim = animations[ i ];
var keyFrameAnim = new THREE.KeyFrameAnimation( anim );
keyFrameAnim.timeScale = 1;
keyFrameAnim.loop = false;
kfAnimations.push( keyFrameAnim );
anim = kfAnimations[i];
anim.play();
}
showroom.position.set(0, 0, -20);
showroom.scale.set(0.06, 0.06, 0.06);
showroom.traverse(function (child) {
child.castShadow = true;
child.receiveShadow = true;
});
showroom.updateMatrix();
scene.add( showroom );
animate();
} );
}
// On Mouse double click event function
function onDoubleClick(event) {
// Set the mouse down flag to false
mouseDown = false;
// Canvas x (left) and y (top) position
var canvasLeft = 0;
var canvasTop = 0;
// "event.clientX" is the mouse x position. "event.clientY" is the mouse y position
var tempX = event.clientX - canvasLeft;
var tempY = event.clientY - canvasTop;
// Create a normalised vector in 2d space
var vector = new THREE.Vector3((tempX / window.innerWidth) * 2 - 1, - (tempY / innerHeight) * 2 + 1, 0.5);
// Unproject a 2D point into the 3D word
// Use the camera projection matrix to transform the vector to the 3D world space
vector.unproject(camera);
// Send a ray in the direction the user has clicked from the cameras position
var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
// Check if the ray has intersected with any objects and get intersections
var intersects = raycaster.intersectObjects(objects, true);
// Check if intersected with objects
if (intersects.length > 0) {
var tempStr = "Number of items: " + intersects.length + " ";
// List the items that were hit
for(var i=0; i < intersects.length; i++){
if(intersects[i].object.name != ""){
// The mesh name set above
tempStr += " | Name: " + intersects[i].object.name;
} else {
// The names inside the model
tempStr += " | Name: " + intersects[i].object.parent.name;
}
}
//Debug information
document.getElementById("debugInfo").innerHTML = tempStr + ".<br>";
//END
}
}
function loopAnimations(){
// Loop through all animations
for ( var i = 0; i < kfAnimationsLength; i++ ) {
// Check if the animation is player and not paused.
if(kfAnimations[i].isPlaying && !kfAnimations[i].isPaused){
if(kfAnimations[i].currentTime == lastFrameCurrentTime[i]) {
kfAnimations[i].stop();
//kfAnimations[i].play();
lastFrameCurrentTime[i] = 0;
}
}
}
}
function play_pauseAnim() {
//checks is there animation and is it paused
if(kfAnimationsLength > 0) {
if(kfAnimations[0].isPlaying) {
for(i = 0; i < kfAnimationsLength; i++){
kfAnimations[i].stop();
}
}else {
for(i = 0; i < kfAnimationsLength; i++) {
lastFrameCurrentTime[i] = 0;
//kfAnimations[i].play(kfAnimations[i].currentTime);
kfAnimations[i].play(0);
}
}
}
}
function checkTime(){
if(kfAnimationsLength > 0) {
if(kfAnimations[0].isPlaying) {
if(kfAnimations[0].currentTime > 3){
play_pauseAnim();
}
}
}
}
// create a render loop to draw the scene 60 times per second
function render() {
//checkTime();
daeObject.rotation.y += guiControls.rotationY;
//if (daeObject.position.z < 25) {
daeObject.position.z = guiControls.positionZ;
//}
spotLight.rotation.x += guiControls.rotationX;
spotLight.rotation.y += guiControls.rotationY;
spotLight.rotation.z += guiControls.rotationZ;
stats.update();
}
function animate () {
var deltaTime = clock.getDelta();
for ( var i = 0; i < kfAnimationsLength; i++ ) {
// Get a key frame animation.
var anim = kfAnimations[i];
anim.update( deltaTime );
}
loopAnimations();
requestAnimationFrame(animate);
// Update last frame current time.
for ( var i = 0; i < kfAnimationsLength; i++ ) {
lastFrameCurrentTime[i] = kfAnimations[i].currentTime;
}
render();
renderer.render(scene, camera);
}
// Loads skybox texture
function loadTexture(path) {
var texture = new THREE.Texture(texture_placeholder);
var material = new THREE.MeshBasicMaterial({
map: texture,
overdraw: 0.5
});
var image = new Image();
image.onload = function() {
texture.image = this;
texture.needsUpdate = true;
};
image.src = path;
return material;
}
Macast,
Please, check if you haven't forgotten in your code:
var objets = [];
var raycaster = new THREE.Raycaster();
And for each part of the car this line :
objects.push( mesh );
Ex:
var geometry = new THREE.RingGeometry( 1, 5, 32 );
var material = new THREE.MeshBasicMaterial( { color: 0xff0000, side: THREE.DoubleSide } );
var simpleTire = new THREE.Mesh( geometry, material );
simpleTire.name = 'tire';
objects.push( simpleTire );
scene.add( simpleTire );
Then, it's simple :
if ( intersects.length > 0 ) {
switch(intersects[0].object.name){
case 'tire':
console.log('A pretty red tire');
break;
case 'motor':
console.log('An electric motor');
break;
}
}
I've been playing with the interactive cubes and decided to add a click function, which leads to a specific link. Now I would like to add unique text on each block which is rendered as a material. For right now, I am using a single material, so I am thinking I need to create an array for all my possible materials, however whenever I reference that array in the parameters for building the mesh of my objects, I get a setHex() is undefined returned to me in the console. How can I attach a unique material to each cube?
<html lang="en">
<head>
<title>three.js webgl - interactive cubes</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
font-family: Monospace;
background-color: #f0f0f0;
margin: 0px;
overflow: hidden;
}
div#stats{
display:none;
}
p{
position:absolute;
top:500px;
left:500px;
}
</style>
</head>
<body>
<div><p>Hello!</p></div>
<script src="../build/three.min.js"></script>
<script src="js/renderers/Projector.js"></script>
<script src='../build/threex.dynamictexture.js'></script>
<script src="js/libs/stats.min.js"></script>
<script>
var container, stats;
var camera, scene, raycaster, renderer;
var mouse = new THREE.Vector2(), INTERSECTED;
var radius = 100, theta = 0;
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
var info = document.createElement( 'div' );
info.style.position = 'absolute';
info.style.top = '10px';
info.style.width = '100%';
info.style.textAlign = 'center';
// info.innerHTML = 'three.js webgl - interactive cubes';
container.appendChild( info );
camera = new THREE.PerspectiveCamera( 100, window.innerWidth / window.innerHeight, 1, 10000 );
scene = new THREE.Scene();
var light = new THREE.DirectionalLight( 0x6699CC, 0.5 );
light.position.set( 1, 1, 1 ).normalize();
scene.add( light );
raycaster = new THREE.Raycaster();
projector = new THREE.Projector();
renderer = new THREE.WebGLRenderer();
renderer.setClearColor( 0xf0f0f0 );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.sortObjects = false;
container.appendChild(renderer.domElement);
var dynamicTexture = new THREEx.DynamicTexture(512,512);
var dynamicTexture2 = new THREEx.DynamicTexture(512,512);
dynamicTexture.context.font = "bold"+(0.2*512)+ "px Courier";
dynamicTexture.texture.anisotropy = renderer.getMaxAnisotropy();
dynamicTexture2.context.font = "bold"+(0.2*512)+ "px Courier";
dynamicTexture2.texture.anisotropy = renderer.getMaxAnisotropy();
dynamicTexture2.clear('blue');
dynamicTexture.drawTextCooked({
text: 'alltheletters',
lineHeight: 0.2,
})
dynamicTexture.clear('gray');
dynamicTexture.drawTextCooked({
text: 'wow text text text',
lineHeight: 0.2,
})
// var materialArray = [];
// materialArray.push(new THREE.MeshBasicMaterial({map: dynamicTexture2 }))
scene.add(object);
for ( var i = 0; i < 5; i ++ ) {
var object = new THREE.Mesh( geometry, material );
object.position.x = Math.random() * 800 - 400;
object.position.y = Math.random() * 800 - 400;
object.position.z = Math.random() * 800 - 400;
object.rotation.x = Math.random() * 2 * Math.PI;
object.rotation.y = Math.random() * 2 * Math.PI;
object.rotation.z = Math.random() * 2 * Math.PI;
object.scale.x = Math.random() + 0.5;
object.scale.y = Math.random() + 0.5;
object.scale.z = Math.random() + 0.5;
scene.add( object );
var geometry = new THREE.BoxGeometry( 62, 62, 62 );
var material = new THREE.MeshBasicMaterial({
map: dynamicTexture.texture,
color:Math.random() * 0xffffff,
name: "box1",
})
switch (i) {
case 0:
object.userData = {
URL: "http://google.com"
};
break;
case 1:
object.userData = {
URL: "http://yahoo.com"
};
break;
case 2:
object.userData = {
URL: "http://msn.com"
};
break;
case 3:
object.userData = {
URL: "http://engadget.com"
};
break;
case 4:
object.userData = {
URL: "http://stackoverflow.com"
};
break;
}
}
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
container.appendChild( stats.domElement );
document.addEventListener('mousedown', onDocumentMouseDown,false);
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseMove( event ) {
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}
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 raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position)
.normalize());
var intersects = raycaster.intersectObjects(scene.children);
if (intersects.length > 0) {
window.open(intersects[0].object.userData.URL);
}
}
//
function animate() {
requestAnimationFrame( animate );
render();
stats.update();
}
function render() {
theta += 0.1;
camera.position.x = radius * Math.sin( THREE.Math.degToRad( theta ) );
camera.position.y = radius * Math.sin( THREE.Math.degToRad( theta ) );
camera.position.z = radius * Math.cos( THREE.Math.degToRad( theta ) );
camera.lookAt( scene.position );
camera.updateMatrixWorld();
// find intersections
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( scene.children ), material;
// for(var p =0; p < intersects.length; p++){
// }
if (intersects.length > 0) {
if (INTERSECTED != intersects[0].object) {
if (INTERSECTED){
material = INTERSECTED.material;
if(material.emissive){
material.emissive.setHex(INTERSECTED.currentHex);
}
else{
material.color.setHex(INTERSECTED.currentHex);
}
}
INTERSECTED = intersects[0].object;
material = INTERSECTED.material;
if(material.emissive){
INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex();
material.emissive.setHex(0xff0000);
}
else{
INTERSECTED.currentHex = material.color.getHex();
material.color.setHex(0xff0000);
}
console.log(INTERSECTED.position);
}
}
else {
if (INTERSECTED){
material = INTERSECTED.material;
if(material.emissive){
material.emissive.setHex(INTERSECTED.currentHex);
}
else
{
material.color.setHex(INTERSECTED.currentHex);
}
}
INTERSECTED = null;
}
renderer.render( scene, camera );
}
</script>
</body>
</html>
I don't think the problem is the array, its the object in the array. Try this:
var materialArray = [];
materialArray.push(new THREE.MeshBasicMaterial({
map: dynamicTexture2.texture,
color:Math.random() * 0xffffff,
name: "box1",
}));
//...
var object = new THREE.Mesh( geometry, materialArray[0] );
If you want to create multiple cubes with different materials, you'll need to create multiple objects, instead of reusing object. You can wrap all the object creation/positioning code in a function, and have the unique material be a parameter.
I wrote a program to randomly generate and put ConvexGeometry object into a box container based on Physijs's example shapes.
Everything goes well until too many objects are put into the container, objects just struggle to fly out of the box .
Here is the code to initialize scene:
initScene = function() {
TWEEN.start();
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.shadowMapEnabled = true;
renderer.shadowMapSoft = true;
document.getElementById( 'viewport' ).appendChild( renderer.domElement );
render_stats = new Stats();
render_stats.domElement.style.position = 'absolute';
render_stats.domElement.style.top = '0px';
render_stats.domElement.style.zIndex = 100;
document.getElementById( 'viewport' ).appendChild( render_stats.domElement );
physics_stats = new Stats();
physics_stats.domElement.style.position = 'absolute';
physics_stats.domElement.style.top = '50px';
physics_stats.domElement.style.zIndex = 100;
document.getElementById( 'viewport' ).appendChild( physics_stats.domElement );
scene = new Physijs.Scene({ fixedTimeStep: 1 / 120 });
scene.setGravity(new THREE.Vector3( 0, -30, 0 ));
scene.addEventListener(
'update',
function() {
scene.simulate( undefined, 2 );
physics_stats.update();
}
);
camera = new THREE.PerspectiveCamera(
35,
window.innerWidth / window.innerHeight,
1,
1000
);
camera.position.set( 150, 120, 150 );
camera.lookAt( scene.position );
scene.add( camera );
// Materials
wall_material = Physijs.createMaterial(
new THREE.MeshBasicMaterial( { color: 0xffffff, wireframe: true, transparent: true, opacity: 1 } )
);
ground_material = Physijs.createMaterial(
new THREE.MeshLambertMaterial({ map: THREE.ImageUtils.loadTexture( 'images/rocks.jpg' ) }),
.8, // high friction
.4 // low restitution
);
ground_material.map.wrapS = ground_material.map.wrapT = THREE.RepeatWrapping;
ground_material.map.repeat.set( 2.5, 2.5 );
// Ground
ground = new Physijs.BoxMesh(
new THREE.BoxGeometry(50, 1, 50),
//new THREE.PlaneGeometry(50, 50),
ground_material,
0 // mass
);
ground.receiveShadow = true;
scene.add( ground );
// Bumpers
var bumper,
bumper_geom = new THREE.BoxGeometry(1, 50, 50);
// Back left
bumper = new Physijs.BoxMesh( bumper_geom, ground_material, 0, { restitution: .2 } );
bumper.position.y = 25;
bumper.position.x = -25;
bumper.receiveShadow = true;
bumper.castShadow = true;
scene.add( bumper );
// Front right
bumper = new Physijs.BoxMesh( bumper_geom, wall_material, 0, { restitution: .2 } );
bumper.position.y = 25;
bumper.position.x = 25;
bumper.receiveShadow = true;
bumper.castShadow = true;
scene.add( bumper );
// Back right
bumper = new Physijs.BoxMesh( bumper_geom, wall_material, 0, { restitution: .2 } );
bumper.position.y = 25;
bumper.position.z = -25;
bumper.rotation.y = Math.PI / 2;
bumper.receiveShadow = true;
bumper.castShadow = true;
scene.add( bumper );
// Front left
bumper = new Physijs.BoxMesh( bumper_geom, wall_material, 0, { restitution: .2 } );
bumper.position.y = 25;
bumper.position.z = 25;
bumper.rotation.y = Math.PI / 2;
bumper.receiveShadow = true;
bumper.castShadow = true;
scene.add( bumper );
// Top
bumper = new Physijs.BoxMesh( bumper_geom, wall_material, 0, { restitution: .2 } );
bumper.position.y = 50;
bumper.position.x = 0;
bumper.position.z = 0;
bumper.rotation.z = Math.PI / 2;
bumper.receiveShadow = true;
bumper.castShadow = true;
scene.add( bumper );
requestAnimationFrame( render );
scene.simulate();
createShape();
};
Create shapes:
createShape = (function() {
var addshapes = true,
doCreateShape;
setTimeout(
function addListener() {
var button = document.getElementById( 'stop' );
if ( button ) {
button.addEventListener( 'click', function() { addshapes = false; } );
} else {
setTimeout( addListener );
}
}
);
doCreateShape = function() {
var shape, material = new THREE.MeshLambertMaterial({ opacity: 0, transparent: true });
var minR = 5, maxR = 10;
var minVertices = 4, maxVertices = 6;
var sphere = randomSphereInBox(minR, maxR, {vertex3A: new THREE.Vector3(0, 0, 0), vertex3B: new THREE.Vector3(50, 50, 50)});
var convex_geometry = randomConvexInSphere(minVertices, maxVertices, sphere);
shape = new Physijs.BoxMesh(
convex_geometry,
material
);
shape.material.color.setRGB( Math.random() * 100 / 100, Math.random() * 100 / 100, Math.random() * 100 / 100 );
shape.castShadow = false;
shape.receiveShadow = false;
shape.position.set(
Math.random() * 30 - 15,
40,
Math.random() * 30 - 15
);
if ( addshapes ) {
shape.addEventListener( 'ready', createShape );
}
scene.add( shape );
new TWEEN.Tween(shape.material).to({opacity: 1}, 500).start();
document.getElementById( 'shapecount' ).textContent = ( ++shapes ) + ' shapes created';
};
return function() {
setTimeout( doCreateShape, 10 );
};
})();
Is there any way to constrain all the objects within the container?
Well, the fact that the objects are bouncing out of the box is because you are overloading it to the point where the objects altogether are "too big" for the containement box, leading to leaks. I don't think there is a real fix to this as this is always a problem in physics engines, but it doesn't hurt to try and plug a hole, right?
You could try using the object's own addEventListener method to check for collisions with the containement box and if so, set the position of itself to the center of the containement box, avoiding any extra collisions for a while:
doCreateShape = function(){
// ...
shape.addEventListener('collision', function(collidedWith, lineVel, angleVel){
if(collidedWith.material == wall_material){ // If it actually collided with a wall
this.__dirtyPosition = true; // To actively change the object position
this.position.x = 0;
this.position.y = 40;
this.position.z = 0;
}
});
scene.add(shape);
// ...
}
Just make sure that the wall_material variable is global.
Also, setting the restitution of all objects to 0 will lessen bouncing inside the container.