I'm trying to find a way to animate the children in a Collada model of the Canadarm2. The model contains 7 arm segments whose angles need to be set by reading in rotation angle data from each of 7 spreadsheet columns one row at a time. Each line holds 7 columns of data representing each second's angles in an hour, or longer, mission.
I'm hoping someone might suggest a solution to get me started -- perhaps something like the guesswork I've tried inside the animate() function below.
if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
var container, stats, clock;
var camera, scene, renderer, canadarm2;
init();
animate();
function init() {
container = document.getElementById( 'container' );
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 2000 );
camera.position.set( 8, 10, 80 );
camera.lookAt( new THREE.Vector3( 0, 3, 0 ) );
scene = new THREE.Scene();
clock = new THREE.Clock();
// loading manager
var loadingManager = new THREE.LoadingManager( function() {
scene.add( canadarm2 );
} );
// collada
var loader = new THREE.ColladaLoader( loadingManager );
//loader.load( 'examples/models/collada/elf/elf.dae', function ( collada ) {
loader.load( 'canadarm2.dae', function ( collada ) {
canadarm2 = collada.scene;
} );
//
var ambientLight = new THREE.AmbientLight( 0xcccccc, 0.4 );
scene.add( ambientLight );
var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.8 );
directionalLight.position.set( 1, 1, 0 ).normalize();
scene.add( directionalLight );
//
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
//
stats = new Stats();
container.appendChild( stats.dom );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
/*
//#######################################
// doesn't work
var armEP = canadarm2.getChildByName("EP", true);// EP is one of 7 arms
armEP.position.x += 0.01;
armEP.rotation.y += 45;
//#######################################
*/
render();
stats.update();
}
function render() {
var delta = clock.getDelta();
if ( canadarm2 !== undefined ) {
canadarm2.rotation.z += delta * 0.5;
}
renderer.render( scene, camera );
}
This seems to work although it seems an awfully roundabout way to locate the arms.
scene.traverse(function (child) {
switch(child.name) {
case "SR":
child.rotation.x += 0.01;
break;
case "SP":
child.rotation.y += 0.01;
break;
case "SY":
child.rotation.y += 0.01;
break;
case "EP":
child.rotation.y += 0.01;
break;
case "WP":
child.rotation.x += 0.01;
break;
case "WY":
child.rotation.y += 0.01;
break;
case "WR":
child.rotation.x += 0.01;
break;
}
});
Related
I’m trying to access any property of my imported point-cloud model.
When I console.log the model within the function loader.load it works. The first problem is that I don’t know how to get the same result outside the function. I did it only with the last three lines of code but initializing the object with different name.
I used the code for point-cloud models from three.js docs and everything works fine except when I want to change the size of the points. I guess it is due to the different structure of my model (photo attached).
Here’s the code that I use.
import './style.css'
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { PCDLoader } from 'three/examples/jsm/loaders/PCDLoader'
import { BufferAttribute, BufferGeometry } from 'three';
let camera, scene, renderer;
init();
render();
function init() {
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 30, window.innerWidth / window.innerHeight, 0.01, 40 );
camera.position.set( 0, 0, 7 );
scene.add( camera );
const controls = new OrbitControls( camera, renderer.domElement );
controls.addEventListener( 'change', render ); // use if there is no animation loop
controls.minDistance = 0.5;
controls.maxDistance = 10;
//scene.add( new THREE.AxesHelper( 1 ) );
const loader = new PCDLoader();
loader.load( 'pointcloud.pcd', function ( points ) {
points.geometry.center();
points.geometry.rotateY( Math.PI );
points.geometry.rotateX( Math.PI );
points.geometry.rotateZ( Math.PI );
scene.add( points );
console.log(points)
render();
} );
window.addEventListener( 'resize', onWindowResize );
window.addEventListener( 'keypress', keyboard );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function keyboard( ev ) {
const points = scene.getObjectByProperty( 'pointcloud.pcd' );
switch ( ev.key || String.fromCharCode( ev.keyCode || ev.charCode ) ) {
case '+':
points.material.size *= 1.2;
break;
case '-':
points.material.size /= 1.2;
break;
}
render();
}
function render() {
renderer.render( scene, camera );
}
// I used this to acces the object
const points2 = scene.getObjectByProperty( 'points' );
console.log(points2)
enter image description here
change let camera, scene, renderer; to let camera, scene, renderer, points
to make 'points' global.
because the 'points' object only live inside the function, try create a variable outside the loader.load function, then assign the 'points' object to the new variable before render(); now you can access the 'points' from outside this function
I have a basic json scene exported from the three.js/editor. I want to add a repeated texture wrap to an object in this scene but I do not know how to do so since the only examples I have found add the wrap in the creation of the object.
I have already tried accessing the texture and giving it a wrap, but I think I might need to add a texture to the object from the JavaScript then declare the texture wrap instead of trying to add it to an already loaded texture.
<script>
var camera, scene, renderer;
var mesh;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.z = 400;
scene = new THREE.Scene();
var objectLoader = new THREE.ObjectLoader();
objectLoader.load( "models/cube.json", function ( obj ) {
scene.add( obj )
obj.traverse(function(child) {
if (child instanceof THREE.Mesh) {
child.castShadow = true;
child.receiveShadow = true;
}
});
obj.name = "cube";
obj.position.set(0,0,0);
obj.scale.set(200,200,200);
});
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
//
window.addEventListener( 'resize', onWindowResize, false );
while (scene.getObjectByName('Box 1')){
var texture = scene.getObjectByName('Box 1').material;
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set( 2, 2 );
}
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
if (scene.getObjectByName('Box 1') ){
scene.getObjectByName('Box 1').material.map.offset.x += 0.001;
}
renderer.render( scene, camera );
}
</script>
You need to set the wrap and repeat properties of the texture/map and not of the material. Docs also say it's important to set needsUpdate to true, if wrap settings changed. And you are trying to set the properties before the json is loaded (in a while loop?). You should do it within the load callback.
objectLoader.load( "models/cube.json", function ( obj ) {
scene.add( obj )
obj.traverse(function(child) {
if (child instanceof THREE.Mesh) {
child.castShadow = true;
child.receiveShadow = true;
}
});
obj.name = "cube";
obj.position.set(0,0,0);
obj.scale.set(200,200,200);
var box1 = scene.getObjectByName('Box 1');
if (box1) {
var texture = box1.material.map;
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set( 2, 2 );
texture.needsUpdate = true;
}
});
I have the following code to add a gui and the ability to click on objects.
When I set the controls as controls = new THREE.TrackballControls( camera, renderer.domElement); the GUI works but the Raycaster doesnt seem to work.
If I define the constrols as controls = new THREE.TrackballControls( camera); the raycaster works, however once I click the gui on the corner, then wherever I move the mouse the values of the gui change, while if I close it, the GUI resize with response to mouse movements
Can Anyone give me any hint how to fix this?
At the moment works but I'm able to unclick from the control by simultaneously left and right clicking
This link shows how far I have gone and give you an idea of the problem
http://subsurface.gr/joomla/threejs/StreamFnc_ws.html
Here is the full code:
if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
// global variables
var camera, controls, scene, renderer;
var container, stats;
var raycaster, intersects;
var threshold = 0.05;
var mouse = new THREE.Vector2();
var cube;
// Parameters for GUI
var params = {
AAmin: 0.0,
AAmax: 1000.0
};
// main functions
init();
animate();
function init(){
container = document.createElement( 'div' );
document.body.appendChild( container );
//Setup Camera
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 1000 );
camera.name = 'camera';
camera.position.z = 20;
// Setup world
scene = new THREE.Scene();
scene.fog = new THREE.FogExp2( 0xcccccc, 0.002 );
// Setup lights
var directionalLight = new THREE.DirectionalLight( 0xffffff );
directionalLight.position.set( 1, 1, 1 );
scene.add( directionalLight );
directionalLight.name = 'directionalLight';
var directionalLight1 = new THREE.DirectionalLight( 0x002288 );
directionalLight1.position.set( -1, -1, -1 );
directionalLight1.name = 'directionalLight1';
scene.add( directionalLight1 );
var ambientLight = new THREE.AmbientLight( 0x222222 );
ambientLight.name = 'ambientLight';
scene.add( ambientLight );
raycaster = new THREE.Raycaster();
raycaster.params.Points.threshold = threshold;
// Main Scene
var geometry = new THREE.BoxGeometry( 1, 1, 1 );
var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
cube = new THREE.Mesh( geometry, material );
cube.name = 'mycube';
scene.add( cube );
// GUI parameter
var gui = new dat.GUI();
gui.add( params, 'AAmin', -1000, 500 );
gui.add( params, 'AAmax', 500, 2000 );
gui.open();
//renderer
renderer = new THREE.WebGLRenderer( { antialias: false } );
renderer.setClearColor( scene.fog.color );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
//controls
controls = new THREE.TrackballControls( camera);
controls.rotateSpeed = 1.0;
controls.zoomSpeed = 1.2;
controls.panSpeed = 0.8;
controls.noZoom = false;
controls.noPan = false;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.3;
controls.keys = [ 65, 83, 68 ];
controls.addEventListener( 'change', render);
stats = new Stats();
container.appendChild( stats.dom );
// events
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
render();
}
function animate() {
requestAnimationFrame( animate );
render();
controls.update();
}
function render() {
renderer.render( scene, camera );
stats.update();
}
function onDocumentMouseDown( event ){
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
intersects = raycaster.intersectObject( cube );
if ( intersects.length > 0 ){
console.log("You click a cube!");
}
}
If I understand correctly, the TrackballControls is stopping propagation of the mouseDown event (bound to the renderer.domElement) -- meaning your onDocumentMouseDown handler isn't being invoked. Try moving your document.addEventListener(...) to come before new THREE.TrackballControls(...).
Responding to your comment below:
I see the problem now. There appears to be an incompatibility between TrackballControls and Dat.GUI in that the TrackballControls mouseup events stops propagation of the event, which causes Dat.GUI to bug out and get stuck resizing the UI for some reason.
I believe you can solve this by disabling TrackballControls when clicking the GUI, and re-enabling TrackballControls on mouseup:
gui.domElement.addEventListener( 'mousedown', function(){ controls.enabled = false; }, false );
document.addEventListener( 'mouseup', function(){ controls.enabled = true; }, false );
I am fairly new to three.js and have a problem I can't readily find an answer for.
Here is a codepen that should sum up the situation: http://codepen.io/anon/pen/PPYPzO
var container, stats;
var camera, controls, scene, renderer, raycaster, mouse;
init();
animate();
add_world();
var indie_render = true;
for(var j = 0; j < 20; j++){
add_objects(20);
indie_render = !indie_render;
console.log("adding more objects...");
if(!indie_render){render();}
}
function add_world(){
var geometry = new THREE.BoxGeometry( 1000, 1000, 1000);
var mesh = new THREE.MeshBasicMaterial( {color: 0xf5f5dc, wireframe: false, opacity: 0.2, transparent:true } );
var world = new THREE.Mesh( geometry, mesh );
scene.add( world );
render();
}
function add_objects(num, indiv){
var geometry = new THREE.SphereGeometry( 5, 32,32 );
var material = new THREE.MeshBasicMaterial( { shading: THREE.FlatShading } );
material.color.setRGB( Math.random(), Math.random(), Math.random() );
for ( var i = 0; i < num; i ++ ) {
var mesh = new THREE.Mesh( geometry, material );
mesh.position.x = ( Math.random() - 0.5 ) * 1000;
mesh.position.y = ( Math.random() - 0.5 ) * 1000;
mesh.position.z = ( Math.random() - 0.5 ) * 1000;
mesh.updateMatrix();
mesh.matrixAutoUpdate = false;
scene.add( mesh );
if(indie_render){
console.log("individual render");
render();
}
}
}
function init() {
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 2000 );
camera.position.set(500, 500, -1000);
camera.up.set( 0, 1, 0 );
camera.lookAt(500,500,500);
controls = new THREE.OrbitControls( camera );
controls.addEventListener( 'change', render );
//world
scene = new THREE.Scene();
// lights
light = new THREE.DirectionalLight( 0xffffff );
light.position.set( 1, 1, 1 );
scene.add( light );
light = new THREE.DirectionalLight( 0x002288 );
light.position.set( -1, -1, -1 );
scene.add( light );
light = new THREE.AmbientLight( 0x222222 );
scene.add( light );
renderer = new THREE.WebGLRenderer( { antialias: false } );
renderer.setClearColor( 0x000000, 1 );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.sortObjects = false;
container = document.getElementById( 'container' );
container.appendChild( renderer.domElement );
mouse = new THREE.Vector2();
raycaster = new THREE.Raycaster();
container.addEventListener( 'mousemove', onMouseMove, false );
container.addEventListener( 'mousedown', onMouseDown, false );
window.addEventListener( 'resize', onWindowResize, false );
}
function animate() {
requestAnimationFrame( animate );
controls.update();
}
function render() {
renderer.render( scene, camera );
}
function onMouseMove( e ) {
mouse.x = ( e.clientX / renderer.domElement.width ) * 2 - 1;
mouse.y = - ( e.clientY / renderer.domElement.height ) * 2 + 1;
}
function onMouseDown( e ) {
mouse.x = ( e.clientX / renderer.domElement.width ) * 2 - 1;
mouse.y = - ( e.clientY / renderer.domElement.height ) * 2 + 1;
if(e.button == 2){ //right button
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( scene.children, true );
if ( intersects.length > 0 ) {
var geometry = new THREE.SphereGeometry( 5, 32,32 );
var material = new THREE.MeshBasicMaterial( { color:0xff0000, shading: THREE.FlatShading } );
var mesh = new THREE.Mesh( geometry, material );
mesh.position.set(intersects[0].point.x, intersects[0].point.y, intersects[0].point.z);
scene.add(mesh);
render();
}
}
}
In this demo, I init() and animate() a blank scene, and then add a translucent cube, following what seems to be convention. Then I add groups of spheres to the scene in a nested for loop, randomly placing the spheres inside the cube and making a render() call after every scene.add() call.
Currently, the scene adds all the spheres and only then is visible to the user, even though I can add individual objects after the for-loop objects are added (by right-clicking on the cube). I need for the user to be able to watch as spheres are added, rather than waiting for the whole thing to be done.
I realize this may not be the most efficient way to render the scene, but it would be quite helpful if, for example, the info on the objects to be animated is arriving asynchronously from a server. Does anyone have a suggestion?
Thanks
1) First: move call render() to animate:
function animate() {
requestAnimationFrame( animate );
render();
controls.update();
}
2) Call add_objects asynchronously: setTimeout( add_objects, 0, 20 );
http://codepen.io/anon/pen/bVbEEP
I'm using the MTLloader of the three.js library and I don't know why but the texture is not load. The mesh becomes completely white.
However, if I use the OBJ file provided by the examples of the three.js library, the object is loaded with its texture correctly.
Any idea?
I post the code:
function loadObj(urlObj, urlObjMtl, urlHairCompl){
var container, stats;
var camera, scene, renderer;
var mouseX = 0, mouseY = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 2000 );
camera.position.z = 100;
controls = new THREE.TrackballControls( camera );
controls.rotateSpeed = 2.0;
controls.zoomSpeed = 4;
controls.panSpeed = 2;
controls.noZoom = false;
controls.noPan = false;
controls.staticMoving = false;
controls.dynamicDampingFactor = 0.3;
// scene
scene = new THREE.Scene();
var ambient = new THREE.AmbientLight( 0x444444 );
scene.add( ambient );
var directionalLight = new THREE.DirectionalLight( 0xffeedd );
directionalLight.position.set( 0, 0, 1 ).normalize();
scene.add( directionalLight );
// model
var loader = new THREE.OBJMTLLoader();
if (urlObj !== null){
loader.load( urlObj, urlObjMtl, function ( object ) {
object.position.y = 0;
object.position.z = -10;
scene.add( object );
} );
}
var display = document.getElementById('display') ;
renderer = new THREE.WebGLRenderer( { canvas: display } );
// window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
controls.handleResize();
}
function animate() {
//requestAnimationFrame( animate );
//render();
requestAnimationFrame( animate );
controls.update();
renderer.render( scene, camera );
}
function render() {
camera.position.x += ( mouseX - camera.position.x ) * .05;
camera.position.y += ( - mouseY - camera.position.y ) * .05;
camera.lookAt( scene.position );
renderer.render( scene, camera );
}
}
Thanks a lot
Open in txt editor your .obj file and search for string reference -nan inside of it.
Replace the -nan value with 0.000000. Sometimes the .obj file get -nan when exported and than mapping textures fails in three.js.
Example:
vn -nan -nan -nan" should be "vn 0.000000 0.000000 0.000000
That was my case. With -nan it will load with OBJLoader, but when added the .mtl [OBJMTLLoader] it was failing. So fix for me was replacing the -nan with 0.000000.