Three.js fat line hilbert3D to points - three.js

Im trying to implement line with thinkness and I found this example
However the example uses:
var points = GeometryUtils.hilbert3D( new THREE.Vector3( 0, 0, 0 ), 20.0, 1, 0, 1, 2, 3, 4, 5, 6, 7 );
I do not want to use this and instead i want to create the line with an Array of Vector3 points.
var geometry = new LineGeometry();
geometry.setPositions( positions );
geometry.setColors( colors );
matLine = new LineMaterial( {
color: 0xffffff,
linewidth: 5, // in pixels
vertexColors: true,
//resolution: // to be set by renderer, eventually
dashed: false
} );
line = new Line2( geometry, matLine );
line.computeLineDistances();
line.scale.set( 1, 1, 1 );
scene.add( line );
Basically, in the example it uses positions, I want to use points instead.
Thanks

It's not possible to pass an array of THREE.Vector3() to THREE.LineGeometry. However, you just have to convert your data to this pattern [ x1, y1, z1, x2, y2, z2, ... ] and the setup should work fine.
let camera, scene, renderer;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.1, 10 );
camera.position.set( 0.5, 0.5, 2 );
scene = new THREE.Scene();
const points = [
0, 0, 0,
1, 0, 0,
1, 1, 0,
0, 1, 0
];
const geometry = new THREE.LineGeometry();
geometry.setPositions( points );
const material = new THREE.LineMaterial( { color: 0xffffff, linewidth: 2 } );
material.resolution.set( window.innerWidth, window.innerHeight );
const lines = new THREE.Line2( geometry, material );
scene.add( lines );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
}
function animate() {
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
body {
margin: 0;
}
canvas {
display: block;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.115/build/three.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.115/examples/js/lines/LineSegments2.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.115/examples/js/lines/Line2.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.115/examples/js/lines/LineMaterial.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.115/examples/js/lines/LineSegmentsGeometry.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.115/examples/js/lines/LineGeometry.js"></script>

Related

How to animate camera.lookAt using gsap?

camera.lookAt(myObject) will instantly rotate the three.js camera towards the given object.
I would like to animate this rotation using gsap. I have no problem using gsap to animate a change in camera position, but the camera rotation code below does nothing.
const targetOrientation = myObject.quaternion.normalize();
gsap.to({}, {
duration: 2,
onUpdate: function() {
controls.update();
camera.quaternion.slerp(targetOrientation, this.progress());
}
});
How can I animate a camera rotation in this way?
OK this is now fixed. The main problem was a controls.update() line in my render() function. Orbit controls do not work well with camera rotation so you need to make sure that they are completely disabled during the animation.
My revised code that includes rotation and position animations:
const camrot = {'x':camera.rotation.x,'y':camera.rotation.y,'z':camera.rotation.z}
camera.lookAt(mesh.position);
const targetOrientation = camera.quaternion.clone().normalize();
camera.rotation.x = camrot.x;
camera.rotation.y = camrot.y;
camera.rotation.z = camrot.z;
const aabb = new THREE.Box3().setFromObject( mesh );
const center = aabb.getCenter( new THREE.Vector3() );
const size = aabb.getSize( new THREE.Vector3() );
controls.enabled = false;
const startOrientation = camera.quaternion.clone();
gsap.to({}, {
duration: 2,
onUpdate: function() {
camera.quaternion.copy(startOrientation).slerp(targetOrientation, this.progress());
},
onComplete: function() {
gsap.to( camera.position, {
duration: 8,
x: center.x,
y: center.y,
z: center.z+4*size.z,
onUpdate: function() {
camera.lookAt( center );
},
onComplete: function() {
controls.enabled = true;
controls.target.set( center.x, center.y, center.z);
}
} );
}
});
Try it like so:
let mesh, renderer, scene, camera, controls;
init();
animate();
function init() {
// renderer
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setPixelRatio( window.devicePixelRatio );
document.body.appendChild( renderer.domElement );
// scene
scene = new THREE.Scene();
// camera
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.set( 20, 10, 20 );
camera.lookAt( 0, 0, 0 );
// ambient
scene.add( new THREE.AmbientLight( 0x222222 ) );
// light
const light = new THREE.DirectionalLight( 0xffffff, 1 );
light.position.set( 20,20, 0 );
scene.add( light );
// axes
scene.add( new THREE.AxesHelper( 20 ) );
// geometry
const geometry = new THREE.SphereBufferGeometry( 5, 12, 8 );
// material
const material = new THREE.MeshPhongMaterial( {
color: 0x00ffff,
flatShading: true,
transparent: true,
opacity: 0.7,
} );
// mesh
mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
const startOrientation = camera.quaternion.clone();
const targetOrientation = mesh.quaternion.clone().normalize();
gsap.to( {}, {
duration: 2,
onUpdate: function() {
camera.quaternion.copy(startOrientation).slerp(targetOrientation, this.progress());
}
} );
}
function animate() {
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.123/build/three.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.4.0/gsap.min.js"></script>
You have to ensure to call Quaternion.slerp() always with the start orientation and not with camera.quaternion. Otherwise the interpolation will be incorrect.

Three.js : using SetTimeOut to animate position of a light spawned on scene 5 seconds after startup

I succeed to spawn a light on my scene after a delay using SetTimeOut. This is working very great.
But in my case, I would like to add animation properties to this light. How can I do this ? If I write animation properties for an element that doesn't exist, three.js is crashing on launch. If I write a SetTimeOut property (like I did for my light in my scene), I'm getting this error on the browser console : "light2 is not defined".
Here is an example :
var scene = new THREE.Scene();
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
camera.position.set( 0, 0, 10);
var light = new THREE.PointLight( 0xffffff, 1, 100 );
light.position.set( -150, 0, 15 );
scene.add( light );
var geometry = new THREE.BoxGeometry( 1.5, 1.5, 1.5 );
var material = new THREE.MeshPhysicalMaterial( { color: 0xffffff } );
var cube = new THREE.Mesh( geometry, material );
scene.add( cube );
setTimeout(function() {
var light2 = new THREE.PointLight( "hsl(358, 100%, 45%)", 1, 100 );
light2.position.set( 0, 0, 15 );
scene.add( light2 );
}, 5000);
var animate = function () {
requestAnimationFrame( animate );
cube.rotation.x += 0.05;
cube.rotation.y += 0.05;
light.position.x += 0.5;
setTimeout(function() {
light2.position.x += 0.5;
}, 5001);
renderer.render(scene, camera);
};
animate();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/100/three.min.js"></script>
<head>
<meta charset="utf-8">
<style>
body { margin: 0; }
canvas { width: 100%; height: 100% }
</style>
</head>
<body>
</body>
Thanks a lot.
Declare var light2; in outer scope:
var light2;
setTimeout(function() {
light2 = new THREE.PointLight( "hsl(358, 100%, 45%)", 1, 100 );
light2.position.set( 0, 0, 15 );
scene.add( light2 );
}, 5000);
and then check if it is set before using it:
setTimeout(function() {
if (light2) {
light2.position.x += 0.5;
}
}, 5001);

three.js Mesh not displaying rectangle depending on orientation

I have created a MWE that creates a single rectangle spinning. However, the rectangle disappears based on its orientation, and the material (which claims to be dotted lines) does not work and instead the rectangle is drawn in solid white.
I suspect the disappearance is due to rectangles only being visible facing the camera. Is there a simple two-sided rectangle parameter?
Why isn't the rectangle being drawn as a dashed outline?
var container, stats;
var camera, scene, renderer;
var group;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
function init( ) {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.set( 0, 150, 500 );
scene = new THREE.Scene();
var lineDash = new THREE.LineDashedMaterial( { color: 0xffaa00, dashSize: 3, gapSize: 1, linewidth: 2 } );
var wall = new THREE.Geometry();
var h = 200;
wall.vertices.push(new THREE.Vector3(0, 0, 0));
wall.vertices.push(new THREE.Vector3(200, 0, 0));
wall.vertices.push(new THREE.Vector3(200, 0, h));
wall.vertices.push(new THREE.Vector3(0, 0, h));
wall.faces.push( new THREE.Face3( 0, 1, 2 ) );
wall.faces.push( new THREE.Face3( 0, 2, 3 ) );
var wallObj = new THREE.Mesh(wall, lineDash );
wallObj.position.x = 0;
wallObj.position.y = 200;
wallObj.rotation.x = Math.PI/2;
group = new THREE.Group();
group.add(wallObj);
scene.add( group );
renderer = new THREE.CanvasRenderer();
renderer.setClearColor( 0xf0f0f0 );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
group.rotation.y += .05;
renderer.render( scene, camera );
}
init( );
animate();
To make a material double-sided, set
material.side = THREE.DoubleSide.
LineDashedMaterial requires line distances to be computed.
geometry.computeLineDistances().
WebGLRenderer is preferable to CanvasRenderer.
three.js r.75

Three.js: create a square cone with radius segments more than 4

I have done some math to create a square cone with radius segment
more than 4. When the top and bottom radius are the same it works fine as shown in the image.
but when the top and bottom radius are different it does not work as shown in the image.
I am not sure what the problem is? I really appreciate any other way to do this? Thank you in advance. here is my code:
<html>
<head>
<title>Lightshade</title>
<script src="three.js"> </script>
<script src="TrackballControls.js"></script>
</head>
<body>
<script>
//declaring variables
var camera, scene, renderer;
var controls;
var cone, coneGeometry;
scene = new THREE.Scene();
var camera = new THREE.OrthographicCamera( -window.innerWidth / 2.5, window.innerWidth / 2.5, window.innerHeight / 2.5, -window.innerHeight / 2.5, -10000, 1000000);
camera.position.set( 0, 2.0, 5.0);
camera.lookAt(scene.position);
//adding the renderer to the screen
renderer = new THREE.WebGLRenderer( { antialias: true} );
renderer.setClearColor( 0xeeeeee , 0); //eeeeee
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.shadowMapCullFace = THREE.CullFaceBack;
renderer.shadowMapEnabled = true;
document.body.appendChild( renderer.domElement );
//adding the camera interactive method
controls = new THREE.TrackballControls( camera, renderer.domElement );
controls.noKeys = true;
//creating materials for lightshade
lightshadeMaterial = new THREE.MeshPhongMaterial({color:0xffeb00,wireframe:false, side:THREE.DoubleSide, ambient: 0xffffff});
var coneGeometry = new THREE.CylinderGeometry( 50, 125, 150, 60, 44 , true);
var cone = new THREE.Mesh( coneGeometry, lightshadeMaterial );
cone.geometry.applyMatrix(new THREE.Matrix4().makeRotationY(Math.PI/2));
for (j=0;j<=2684;j=j+61) {
for (i=0;i<=60;i++) {
if (i<=15) {
tempLineGrad = (cone.geometry.vertices[15].z - cone.geometry.vertices[0].z) / (cone.geometry.vertices[15].x - cone.geometry.vertices[0].x);
cone.geometry.vertices[i+j].z = tempLineGrad * (cone.geometry.vertices[i+j].x - cone.geometry.vertices[0].x) + cone.geometry.vertices[0].z;
}
if (i>15 && i<=30){
tempLineGrad = (cone.geometry.vertices[30].z - cone.geometry.vertices[15].z) / (cone.geometry.vertices[30].x - cone.geometry.vertices[15].x);
if (tempLineGrad>999999) {
cone.geometry.vertices[i+j].x = cone.geometry.vertices[15].x;
} else {
cone.geometry.vertices[i+j].z = tempLineGrad * (cone.geometry.vertices[i+j].x - cone.geometry.vertices[15].x) + cone.geometry.vertices[15].z;
}
}
if (i>30 && i<=45){
tempLineGrad = (cone.geometry.vertices[45].z - cone.geometry.vertices[30].z) / (cone.geometry.vertices[45].x - cone.geometry.vertices[30].x);
cone.geometry.vertices[i+j].z = tempLineGrad * (cone.geometry.vertices[i+j].x - cone.geometry.vertices[30].x) + cone.geometry.vertices[30].z;
}
if (i>45 && i<=60){
tempLineGrad = (cone.geometry.vertices[60].z - cone.geometry.vertices[45].z) / (cone.geometry.vertices[60].x - cone.geometry.vertices[45].x);
cone.geometry.vertices[i+j].z = tempLineGrad * (cone.geometry.vertices[i+j].x - cone.geometry.vertices[45].x) + cone.geometry.vertices[45].z;
}
}
}
scene.add(cone);
// adding some light to the screen
var light3 = new THREE.PointLight( 0xffffff, 1, 1000);
light3.position.set( 0, 300.0, 0 );
scene.add( light3 );
var light1 = new THREE.PointLight( 0xffffff,0.7, 1000 );
light1.position.set( 0, 4.0, 0 );
scene.add( light1 );
var light2 = new THREE.PointLight( 0xffffff, 0.7, 1000 );
light2.position.set( 0, -1.0, 0 );
scene.add( light2 );
var light4 = new THREE.PointLight( 0xffffff, 1, 1000);
light4.position.set( 300.0, 160.0, 300.0 );
scene.add( light4 );
var light5 = new THREE.PointLight( 0xffffff, 1, 1000);
light5.position.set( -300.0, 160.0, 300.0 );
scene.add( light5 );
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
controls.update();
renderer.render( scene, camera );
}
animate();
</script>
</body>
</html>

Three JS how to render the xyz axis over all the models inside a scene?

Currently I want to render the axis over the cube (rather than through it). But I can't figure out a way to do that. Does any one know how to render the axis lines over the cube module? Thanks.
Have provided the JS code below.
The cube will be rendered in scene and axis lines will be rendered in fscene. (The axis lines rendered in fscene is supposed to cover the cube which rendered in scene)
var container, stats;
var camera, scene, fscene, renderer;
var cube, plane;
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.01, 999999 );
camera.position.y = 5;
camera.position.z = 5;
camera.position.x = 4;
camera.lookAt(new THREE.Vector3());
scene = new THREE.Scene();
fscene = new THREE.Scene();
// Cube
var geometry = new THREE.CubeGeometry( 1, 1, 1 );
for ( var i = 0; i < geometry.faces.length; i ++ ) {
geometry.faces[ i ].color.setHex( 0xcccccc * i );
}
var material = new THREE.MeshBasicMaterial( { vertexColors: THREE.FaceColors } );
cube = new THREE.Mesh( geometry, material );
scene.add(cube);
addAxis();
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.autoClear = false;
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
animate();
}
function animate() {
requestAnimationFrame(animate);
renderer.clear();
renderer.render(scene, camera);
renderer.render(fscene, camera);
}
function addAxis() {
var sceneSize = 9000;
line({ begin: [0, 0, 0], end: [sceneSize, 0, 0], color: 0xff0000, scene: scene });
line({ begin: [0, 0, 0], end: [-sceneSize, 0, 0], color: 0xff0000, dashed: true, scene: scene });
line({ begin: [0, 0, 0], end: [0, sceneSize, 0], color: 0x00ff00, scene: scene });
line({ begin: [0, 0, 0], end: [0, -sceneSize, 0], color: 0x00ff00, dashed: true, scene: scene });
line({ begin: [0, 0, 0], end: [0, 0, sceneSize], color: 0x0000ff, scene: scene });
line({ begin: [0, 0, 0], end: [0, 0, -sceneSize], color: 0x0000ff, dashed: true, scene: scene });
}
function line(cfg){
var p = cfg.begin;
var q = cfg.end;
if (cfg.color) {
cfg.colorb = cfg.colorb || cfg.color;
cfg.colore = cfg.colore || cfg.color;
}
var geometry = new THREE.Geometry();
var material = cfg.dashed ? new THREE.LineDashedMaterial({ linewidth: 1, color: cfg.color, dashSize: 1, gapSize: 1, depthWrite:false }) : new THREE.LineBasicMaterial({ vertexColors: THREE.VertexColors, depthWrite:false });
var cp = new THREE.Color(cfg.colorb);
var cq = new THREE.Color(cfg.colore);
geometry.vertices.push(new THREE.Vector3(p[0], p[1], p[2]));
geometry.vertices.push(new THREE.Vector3(q[0], q[1], q[2]));
geometry.colors.push(cp,cq);
geometry.computeLineDistances();
var line = new THREE.Line(geometry, material, THREE.LinePieces);
fscene.add(line);
}
TransformControls.js contains the code that might help you.
depthTest should be set to false and transparent should be set to true.

Resources