how to use three.js sky in forge - three.js

I want to build sky scene in forge viewer,I learn the way How can you add text to the Forge Viewer with three.js?So I use threejs-full-es6 to import Sky,and then new sky,but when I add sky object in scene.It report errors: (THREE.Object3D.add: object not an instance of THREE.Object3D). I found new sky by this way the object is different from commen three.js. Through import sky from threejs-full-es6,the obj is only mesh message.But commen three.js sky obj include mesh and uniforms.forge error:
three.js:
import { Sky,SphereBufferGeometry} from './Three.es.min';
initSky() {
var sky, sunSphere;
var scene = viewer.impl.scene;
sky = new Sky();
scene.add( sky );
// Add Sun Helper
sunSphere = new THREE.Mesh(
new SphereBufferGeometry( 20000, 16, 8 ),
new THREE.MeshBasicMaterial( { color: 0xffffff } )
);
sunSphere.position.y = - 700000;
sunSphere.visible = false;
scene.add( sunSphere );
/// GUI
var effectController = {
turbidity: 10,
rayleigh: 2,
mieCoefficient: 0.005,
mieDirectionalG: 0.8,
luminance: 1,
inclination: 0.49, // elevation / inclination
azimuth: 0.25, // Facing front,
sun: ! true
};
var distance = 400000;
function guiChanged() {
var uniforms = sky.uniforms;
uniforms.turbidity.value = effectController.turbidity;
uniforms.rayleigh.value = effectController.rayleigh;
uniforms.luminance.value = effectController.luminance;
uniforms.mieCoefficient.value = effectController.mieCoefficient;
uniforms.mieDirectionalG.value = effectController.mieDirectionalG;
var theta = Math.PI * ( effectController.inclination - 0.5 );
var phi = 2 * Math.PI * ( effectController.azimuth - 0.5 );
sunSphere.position.x = distance * Math.cos( phi );
sunSphere.position.y = distance * Math.sin( phi ) * Math.sin( theta );
sunSphere.position.z = distance * Math.sin( phi ) * Math.cos( theta );
sunSphere.visible = effectController.sun;
sky.uniforms.sunPosition.value.copy( sunSphere.position );
renderer.render( scene, camera );
}
guiChanged();
}

Related

THREE.js , texture not displayed on a sprite

I'm trying to modify the canves/lines.js example and to display textured sprites instead of dots. I load a texture from a PNG file and I have replaced regular dots with sprites. They do not show.
function init() {
var container, separation = 100, amountX = 50, amountY = 50,
particles, particle,sprite;
var crateTexture = THREE.ImageUtils.loadTexture( 'images/crate.png' );
var crateMaterial = new THREE.SpriteMaterial( { map: crateTexture, useScreenCoordinates: false, color: 0xff0000 } );
container = document.createElement('div');
document.body.appendChild(container);
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.z = 100;
scene = new THREE.Scene();
renderer = new THREE.CanvasRenderer();
renderer.setClearColor (0x5bb0d2, 1);
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
// particles
var PI2 = Math.PI * 2;
var material = new THREE.SpriteCanvasMaterial( {
color: 0xfffffff,
program: function ( context ) {
context.beginPath();
context.arc( 0, 0, 0.5, 0, PI2, true );
context.fill();
}
} );
var geometry = new THREE.Geometry();
var x,y,z=0;
for ( var i = 0; i < 100; i ++ ) {
particle = new THREE.Sprite( crateMaterial );
particle.position.x = Math.random() * 2 - 1;
particle.position.y = Math.random() * 2 - 1;
particle.position.z = Math.random() * 2 - 1;
particle.position.normalize();
particle.position.multiplyScalar( Math.random() * 10 + 450 );
particle.scale.x = particle.scale.y = 64;
scene.add( particle );
}
what am I doing wrong ?
All I did was correct, there is an issue when reading textures from local computer. The solution is to assign proper privilages to web serwer OR to allow Chrome or other browser to read local files.
in case of Chrome, you can create a shortcut such as this:
"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" chrome --allow-file-access-from-files

Three.JS - Particles orbiting a point in random directions forming a sphere

I have a particle system where all the particles are positioned at the same coordinates and one after another, in random directions, they (should) start orbiting the center of the scene forming a sphere.
What I managed to achieve until now is a group of Vector3 objects (the particles) that one after another start orbiting the center along the Z axis simply calculating their sine and cosine based on the current angle.
I'm not that good at math and I don't even know what to look for precisely.
Here's what I wrote:
var scene = new THREE.Scene();
let container = document.getElementById('container'),
loader = new THREE.TextureLoader(),
renderer,
camera,
maxParticles = 5000,
particlesDelay = 50,
radius = 50,
sphereGeometry,
sphere;
loader.crossOrigin = true;
function init() {
let vw = window.innerWidth,
vh = window.innerHeight;
renderer = new THREE.WebGLRenderer();
renderer.setSize(vw, vh);
renderer.setPixelRatio(window.devicePixelRatio);
camera = new THREE.PerspectiveCamera(45, vw / vh, 1, 1000);
camera.position.z = 200;
camera.position.x = 30;
camera.position.y = 30;
camera.lookAt(scene.position);
scene.add(camera);
let controls = new THREE.OrbitControls(camera, renderer.domElement);
let axisHelper = new THREE.AxisHelper(50);
scene.add(axisHelper);
container.appendChild(renderer.domElement);
window.addEventListener('resize', onResize, false);
}
function onResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function draw() {
sphereGeometry = new THREE.Geometry();
sphereGeometry.dynamic = true;
let particleTexture = loader.load('https://threejs.org/examples/textures/particle2.png'),
material = new THREE.PointsMaterial({
color: 0xffffff,
size: 3,
transparent: true,
blending: THREE.AdditiveBlending,
map: particleTexture,
depthWrite: false
});
for ( let i = 0; i < maxParticles; i++ ) {
let vertex = new THREE.Vector3(radius, 0, 0);
vertex.delay = Date.now() + (particlesDelay * i);
vertex.angle = 0;
sphereGeometry.vertices.push(vertex);
}
sphere = new THREE.Points(sphereGeometry, material);
scene.add(sphere);
}
function update() {
for ( let i = 0; i < maxParticles; i++ ) {
let particle = sphereGeometry.vertices[i];
if ( Date.now() > particle.delay ) {
let angle = particle.angle += 0.01;
particle.x = radius * Math.cos(angle);
if ( i % 2 === 0 ) {
particle.y = radius * Math.sin(angle);
} else {
particle.y = -radius * Math.sin(angle);
}
}
}
sphere.geometry.verticesNeedUpdate = true;
}
function render() {
update();
renderer.render(scene, camera);
requestAnimationFrame(render);
}
init();
draw();
render();
And here's the JSFiddle if you want to see it live:
https://jsfiddle.net/kekkorider/qs6s0wv2/
EDIT: Working example
Can someone please give me a hand?
Thanks in advance!
You want each particle to rotate around a specific random axis. You can either let them follow a parametric equation of a circle in 3D space, or you can make use of THREE.js rotation matrices.
Right now all your particles are rotating round the vector (0, 0, 1). Since your particles start off on the x-axis, you want them all to rotate around a random vector in the y-z plane (0, y, z). This can be defined during the creation of the vertices:
vertex.rotationAxis = new THREE.Vector3(0, Math.random() * 2 - 1, Math.random() * 2 - 1);
vertex.rotationAxis.normalize();
now you can just call the THREE.Vector3.applyAxisAngle(axis, angle) method on each of your particles with the random rotation axis you created each update:
particle.applyAxisAngle(particle.rotationAxis, 0.01);
To sum up, this is how it should look like:
draw():
...
for ( let i = 0; i < maxParticles; i++ ) {
let vertex = new THREE.Vector3(radius, 0, 0);
vertex.delay = Date.now() + (particlesDelay * i);
vertex.rotationAxis = new THREE.Vector3(0, Math.random() * 2 - 1, Math.random() * 2 - 1);
vertex.rotationAxis.normalize();
sphereGeometry.vertices.push(vertex);
}
...
update():
...
for ( let i = 0; i < maxParticles; i++ ) {
let particle = sphereGeometry.vertices[i];
if ( Date.now() > particle.delay ) {
particle.applyAxisAngle(particle.rotationAxis, 0.01);
}
}
...

U-shaped magnet geometry in three.js

I want to create a "U" shaped magnet in three.js. So can I use TubeGeometry for that?
So if this is the code for creating a 3D sin curve. How can I make it as "U" shaped Magnet?
var CustomSinCurve = THREE.Curve.create(
function ( scale ) { //custom curve constructor
this.scale = ( scale === undefined ) ? 1 : scale;
},
function ( t ) { //getPoint: t is between 0-1
var tx = t * 3 - 1.5;
var ty = Math.sin( 2 * Math.PI * t );
var tz = 0;
return new THREE.Vector3( tx, ty, tz ).multiplyScalar(this.scale);
}
);
var path = new CustomSinCurve( 10 );
var geometry = new THREE.TubeGeometry( path, 20, 2, 8, false );
var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
var mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
If the shape of the magnet's profile is not critical (rectangle instead of circle), then you can use THREE.ExtrudeGeometry():
var path = new THREE.Shape(); // create a U-shape with its parts
path.moveTo(-1, 1);
path.absarc(0, 0, 1, Math.PI, Math.PI * 2);
path.lineTo(1, 1);
path.lineTo(.8, 1);
path.absarc(0, 0, .8, Math.PI * 2, Math.PI, true);
path.lineTo(-.8,1);
path.lineTo(-1, 1);
var extOpt = { // options of extrusion
curveSegments: 15,
steps: 1,
amount: .2,
bevelEnabled: false
}
var uGeom = new THREE.ExtrudeGeometry(path, extOpt); // create a geometry
uGeom.center(); // center the geometry
var average = new THREE.Vector3(); // this variable for re-use
uGeom.faces.forEach(function(face){
average.addVectors(uGeom.vertices[face.a], uGeom.vertices[face.b]).add(uGeom.vertices[face.c]).divideScalar(3); // find the average vector of a face
face.color.setHex(average.x > 0 ? 0xFF0000 : 0x0000FF); // set color of faces, depends on x-coortinate of the average vector
});
var uMat = new THREE.MeshBasicMaterial({ vertexColors: THREE.FaceColors }); // we'll use face colors
var u = new THREE.Mesh(uGeom, uMat);
scene.add(u);
jsfiddle example

Three.js Raycasting with Camera as Origin

I'm trying to determine whether a point in space is visible to the camera or hidden behind other objects in the scene. I'm doing this by casting a ray from the position of the camera to that point in space and testing if that ray is intersected by an set of intersectable objects.
My problem is no intersections occur until the camera position itself intersects one of the objects in the set of intersectable objects.
I've created a jsfiddle in which, if an intersection is detected, a line is drawn from the camera position to the position in space i'm testing for visibility. Currently I believe, the line is only draw at specific points where the camera position intersects the set of intersectable objects.
How do I get the intersections to be registered as they should be, without having to have the camera position intersect objects in the set of intersectable objects?
the code:
var container;
var camera, controls, scene, renderer;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.z = 1000;
controls = new THREE.OrbitControls(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);
// world
scene = new THREE.Scene();
var testObject_G = new THREE.CubeGeometry(100, 100, 5);
var testObject_M = new THREE.MeshBasicMaterial({
color: 0xBBBBBB
});
var testObject_Mesh = new THREE.Mesh(testObject_G, testObject_M);
testObject_Mesh.position.x = -150;
scene.add(testObject_Mesh);
var testObject_Mesh2 = new THREE.Mesh(testObject_G, testObject_M);
testObject_Mesh2.position.x = 0;
scene.add(testObject_Mesh2);
var testObject_Mesh3 = new THREE.Mesh(testObject_G, testObject_M);
testObject_Mesh3.position.x = 150;
scene.add(testObject_Mesh3);
scene2 = new THREE.Object3D();
// renderer
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xffffff, 1);
container = document.getElementById('container');
container.appendChild(renderer.domElement);
//
window.addEventListener('resize', onWindowResize, false);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
controls.handleResize();
render();
}
function animate() {
requestAnimationFrame(animate);
controls.update();
}
function render() {
renderer.render(scene, camera);
castRays();
}
function castRays() {
// rays
var direction = new THREE.Vector3(0, 200, -200);
var startPoint = camera.position.clone();
var ray = new THREE.Raycaster(startPoint, direction);
scene.updateMatrixWorld(); // required, since you haven't rendered yet
var rayIntersects = ray.intersectObjects(scene.children, true);
if (rayIntersects[0]) {
console.log(rayIntersects[0]);
var material = new THREE.LineBasicMaterial({
color: 0x0000ff
});
var geometry = new THREE.Geometry();
geometry.vertices.push(new THREE.Vector3(ray.ray.origin.x, ray.ray.origin.y, ray.ray.origin.z));
geometry.vertices.push(new THREE.Vector3(ray.ray.direction.x, ray.ray.direction.y, ray.ray.direction.z));
var line = new THREE.Line(geometry, material);
scene2.add( line );
}
scene.add(scene2);
}
Thank you
For anyone currently seeing this thread, THREE.Projector has been replaced.
Three.js THREE.Projector has been moved to
The code below handles a 3D vector. If you go to the link above, the first commenter provided the code for a 2D vector.
var vector = new THREE.Vector3();
var raycaster = new THREE.Raycaster();
var dir = new THREE.Vector3();
...
if ( camera instanceof THREE.OrthographicCamera ) {
vector.set( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1, - 1 ); // z = - 1 important!
vector.unproject( camera );
dir.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld );
raycaster.set( vector, dir );
} else if ( camera instanceof THREE.PerspectiveCamera ) {
vector.set( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1, 0.5 ); // z = 0.5 important!
vector.unproject( camera );
raycaster.set( camera.position, vector.sub( camera.position ).normalize());
}
var intersects = raycaster.intersectObjects( objects, recursiveFlag );`
Your idea of casting a ray is good, however raycasting in three.js already does this :
mouse.x = ( ( event.clientX - renderer.domElement.offsetLeft ) / renderer.domElement.width ) * 2 - 1;
mouse.y = - ( ( event.clientY - renderer.domElement.offsetTop ) / renderer.domElement.height ) * 2 + 1;
this formula maps a pixel coordinate from Screen Space to a point in Normalized Device Coordinate ( NDC ) Space.
projector.unprojectVector( vector, camera );
maps a point from NDC Space to a point in World space
Raycaster then creates a ray from the camera position through that world point.
Here is your working Fiddle in which I changed the way of raycasting in your scene and this works, all you have to do now is creating a ray with the right coordinates that I provided you.
r.68

Clickable mesh with THREE.lod

I for now work on a 3D engine for an amateur video game, and i want to add to him tons of features to get the best performances & gameplay. But i've got serious problems to get an "on click event" with lod meshes.
For the "lod" part, no problem for now, he's integrated, but i found no solution to apply that exemple with him :
https://stackoverflow.com/a/12808987/3379444
Did i must push the "lod" object, or every mesh individually ?
a part of my code :
var i, j, mesh, lod;
for ( j = 0; j <= 42; j++) {
lod = new THREE.LOD();
//Here, it's just var for place my mesh into a circle
rayonSysteme = Math.floor(Math.random() * 10000) + 2500;
angleSysteme = Math.random() * 360;
for ( i = 0; i < geometry.length; i ++ ) {
mesh = new THREE.Mesh( geometry[ i ][ 0 ], material );
mesh.scale.set( 1.5, 1.5, 1.5 );
mesh.updateMatrix();
mesh.matrixAutoUpdate = false;
//Callback here ?
mesh.callback = function() { console.log( this.name ); }
lod.addLevel( mesh, geometry[ i ][ 1 ] );
}
lod.position.x = rayonSysteme * Math.cos(angleSysteme);
lod.position.y = Math.floor(Math.random() * 1000)-500;
lod.position.z = rayonSysteme * Math.sin(angleSysteme);
//Or here ?
lod.updateMatrix();
lod.matrixAutoUpdate = false;
gravity = drawCircle("XZ", 64, 500, 360);
gravity.position.x = lod.position.x;
gravity.position.y = lod.position.y;
gravity.position.z = lod.position.z;
scene.add( lod );
//objects.push( lod );
scene.add( gravity );
};
Raycaster will test the mesh that should be used based on the distance to the ray origin.
This is the relevant code inside Raycaster:
} else if ( object instanceof THREE.LOD ) {
matrixPosition.setFromMatrixPosition( object.matrixWorld );
var distance = raycaster.ray.origin.distanceTo( matrixPosition );
intersectObject( object.getObjectForDistance( distance ), raycaster, intersects );
}

Resources