I'm trying to animate the TextGeometries to form a shape of box {delay} sphere {delay} and then a cone. Also, change the colors while the transition. I tried the below code for trying to animate the initial state to a box:
var pointsInsideShape = shapes[0].points.attributes.position.array;
for (i = 0; i < MAX_PARTICLES; i++) {
var tl = new TimelineLite();
tl.from(particleGroup.children[i].position, 2, {
x: pointsInsideShape[ index ++ ],
y: pointsInsideShape[ index ++ ],
z: pointsInsideShape[ index ++ ]
})
}
The console throws the error below:
particleGroup.children[i] is undefined
I checked the particleGroup has objects, however I'm getting this error. I'm not sure why this isn't working.
Currently all the particles are in the center, I'm trying to animate them to form a shape of a sphere, then a delay before the next shape animation and so on. Repeat the loop at the end. If someone could point me in the right direction, it'd be much appreciated. Thank you!
Below is the code:
var renderer, camera, scene, light, shapes, triggers, particleCount, particleGroup,
defaultAnimationSpeed, morphAnimationSpeed, colorToStartWith, textGeometries, loader, typeface,
animationVars;
const MAX_PARTICLES = 100,
PARTICLE_SIZE = .65;
var sts = {
config: function() {
shapes = [{
"geoCode": new THREE.BoxBufferGeometry(13, 13, 13),
"textMat": new THREE.MeshPhongMaterial({
color: 0x029894
}),
"color": 0x029894,
"size": [50, 50, 50]
},
{
"geoCode": new THREE.SphereBufferGeometry(25, 33, 33),
"textMat": new THREE.MeshPhongMaterial({
color: 0x8F3985
}),
"color": 0x8F3985,
"size": [25, 33, 33]
},
{
"geoCode": new THREE.ConeBufferGeometry(25, 50, 30),
"textMat": new THREE.MeshPhongMaterial({
color: 0x11659C
}),
"color": 0x11659C,
"size": [25, 50, 30]
}
];
triggers = document.getElementsByTagName('span');
particleGroup = new THREE.Group();
defaultAnimationSpeed = 1;
morphAnimationSpeed = 18;
normalSpeed = (defaultAnimationSpeed / 100)
fullSpeed = (morphAnimationSpeed / 100)
colorToStartWith = '#8F3985';
textGeometries = new Array();
},
initScene: function() {
//Renderer
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setClearColor(0xffffff, 1);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Scene
scene = new THREE.Scene();
// Camera and position
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.y = -25;
camera.position.z = 45;
camera.rotation.x = -.45;
// Lighting
light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(1, 1, 1).normalize();
scene.add(light);
var light2 = new THREE.DirectionalLight(0xffffff, 1.5);
light2.position.set(0, -1, 0);
scene.add(light2);
// Texts
loader = new THREE.FontLoader();
typeface = 'https://raw.githubusercontent.com/7dir/json-fonts/master/fonts/cyrillic/roboto/Roboto_Regular.json';
//particleGroup
particleGroup = new THREE.Group();
particleGroup.position.x = 0;
particleGroup.position.y = -45;
particleGroup.position.z = 0;
scene.add(particleGroup);
},
fullScreen: function() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
},
attachEvents: function() {
window.addEventListener('resize', this.fullScreen, false);
},
addShapes: function() {
for (idx = 0; idx < shapes.length; idx++) {
shapes[idx].geometry = shapes[idx].geoCode.toNonIndexed();
m = new THREE.MeshLambertMaterial({
color: shapes[idx].color,
opacity: 0,
transparent: true,
wireframe: true
});
shapes[idx].geometry.center();
shape = new THREE.Mesh(shapes[idx].geometry, m);
scene.add(shape);
}
},
computePointsInside: function(idx) {
shapes[idx].points = this.fillWithPoints(shapes[idx].geometry, MAX_PARTICLES);
},
addText: function() {
loader.load(typeface, (font) => {
//var x, y, z, index1, index2;
//x = y = z = index1 = index2 = 0;
var m = new THREE.MeshPhongMaterial({
color: 0x8F3985,
opacity: .8,
specular: 0xffffff,
shininess: 100
});
for (i = 0; i < MAX_PARTICLES; i++) {
g = new THREE.TextGeometry(Math.random() < .5 ? '6' : '9', {
font: font,
size: PARTICLE_SIZE,
height: 0.1
});
var text = new THREE.Mesh(g, m);
text.position.x = 0;
text.position.y = 0;
text.position.z = 0;
text.rotation.x = Math.random() * 2 * Math.PI;
text.rotation.y = Math.random() * 2 * Math.PI;
text.rotation.z = Math.random() * 2 * Math.PI;
text.scale.x = (Math.random() * (0.95 - 0.1) + 0.95).toFixed(4);
text.scale.y = (Math.random() * (0.95 - 0.1) + 0.95).toFixed(4);
text.scale.z = (Math.random() * (0.95 - 0.1) + 0.95).toFixed(4);
//var pointsInsideShape = shapes[0].points.attributes.position.array;
//text.position.x = pointsInsideShape[ index2 ++ ];
//text.position.y = pointsInsideShape[ index2 ++ ];
//text.position.z = pointsInsideShape[ index2 ++ ];
particleGroup.add(text);
}
});
},
animationSequence: function() {
var x, y, z, index;
x = y = z = index = 0;
var pointsInsideShape = shapes[0].points.attributes.position.array;
for (i = 0; i < MAX_PARTICLES; i++) {
var tl = new TimelineLite();
tl.from(particleGroup.children[i].position, 2, {
x: pointsInsideShape[index++],
y: pointsInsideShape[index++],
z: pointsInsideShape[index++]
})
}
},
animate: function() {
window.requestAnimationFrame(sts.animate);
particleGroup.rotation.y += 0.005;
renderer.render(scene, camera)
},
fillWithPoints: function(geometry, count) {
var ray = new THREE.Ray()
var size = new THREE.Vector3();
geometry.computeBoundingBox();
var bbox = geometry.boundingBox;
var points = [];
var dir = new THREE.Vector3(1, 1, 1).normalize();
for (var i = 0; i < count; i++) {
var p = setRandomVector(bbox.min, bbox.max);
points.push(p);
}
function setRandomVector(min, max) {
var v = new THREE.Vector3(
THREE.Math.randFloat(min.x, max.x),
THREE.Math.randFloat(min.y, max.y),
THREE.Math.randFloat(min.z, max.z)
);
if (!isInside(v)) {
return setRandomVector(min, max);
}
return v;
}
function isInside(v) {
ray.set(v, dir);
var counter = 0;
var pos = geometry.attributes.position;
var faces = pos.count / 3;
var vA = new THREE.Vector3(),
vB = new THREE.Vector3(),
vC = new THREE.Vector3();
for (var i = 0; i < faces; i++) {
vA.fromBufferAttribute(pos, i * 3 + 0);
vB.fromBufferAttribute(pos, i * 3 + 1);
vC.fromBufferAttribute(pos, i * 3 + 2);
if (ray.intersectTriangle(vA, vB, vC)) counter++;
}
return counter % 2 == 1;
}
return new THREE.BufferGeometry().setFromPoints(points);
},
init: function() {
this.config();
this.initScene();
this.attachEvents();
this.addShapes();
this.computePointsInside(0);
this.computePointsInside(1);
this.computePointsInside(2);
this.addText();
this.animate();
this.animationSequence();
}
}
sts.init();
body {
margin: 0;
overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/109/three.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js" type="text/javascript"></script>
I added a slight delay to animationSequence
setTimeout(() => this.animationSequence(), 1000);
This is not the best approach and ideally you'd wait until particleGroup.children is loaded with data
After bit of investigation I found the culprit.
addText: function() {
loader.load(typeface, (font) => {
Wrap that in Promise
addText: function() {
return new Promise((resolve, reject) => {
loader.load(typeface, (font) => {
Use put rest of logic in then callback
this.addText().then(_ => {
this.animate();
this.animationSequence();
})
Working example
var renderer, camera, scene, light, shapes, triggers, particleCount, particleGroup,
defaultAnimationSpeed, morphAnimationSpeed, colorToStartWith, textGeometries, loader, typeface,
animationVars;
const MAX_PARTICLES = 100, PARTICLE_SIZE = .65;
var sts = {
config: function() {
shapes = [
{
"geoCode": new THREE.BoxBufferGeometry(13, 13, 13),
"textMat": new THREE.MeshPhongMaterial({ color: 0x029894 }),
"color": 0x029894,
"size": [50, 50, 50]
},
{
"geoCode": new THREE.SphereBufferGeometry(25, 33, 33),
"textMat": new THREE.MeshPhongMaterial({ color: 0x8F3985 }),
"color": 0x8F3985,
"size": [25, 33, 33]
},
{
"geoCode": new THREE.ConeBufferGeometry(25, 50, 30),
"textMat": new THREE.MeshPhongMaterial({ color: 0x11659C }),
"color": 0x11659C,
"size": [25, 50, 30]
}
];
triggers = document.getElementsByTagName('span');
particleGroup = new THREE.Group();
defaultAnimationSpeed = 1;
morphAnimationSpeed = 18;
normalSpeed = (defaultAnimationSpeed / 100),
fullSpeed = (morphAnimationSpeed / 100)
colorToStartWith = '#8F3985';
textGeometries = new Array();
},
initScene: function() {
//Renderer
renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setClearColor(0xffffff, 1);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Scene
scene = new THREE.Scene();
// Camera and position
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.y = -25;
camera.position.z = 45;
camera.rotation.x = -.45;
// Lighting
light = new THREE.DirectionalLight( 0xffffff, 1 );
light.position.set( 1, 1, 1 ).normalize();
scene.add(light);
var light2 = new THREE.DirectionalLight( 0xffffff, 1.5 );
light2.position.set( 0, - 1, 0 );
scene.add( light2 );
// Texts
loader = new THREE.FontLoader();
typeface = 'https://raw.githubusercontent.com/7dir/json-fonts/master/fonts/cyrillic/roboto/Roboto_Regular.json';
//particleGroup
particleGroup = new THREE.Group();
particleGroup.position.x = 0;
particleGroup.position.y = -45;
particleGroup.position.z = 0;
scene.add(particleGroup);
},
fullScreen: function() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
},
attachEvents: function() {
window.addEventListener('resize', this.fullScreen, false);
},
addShapes: function() {
for (idx = 0; idx < shapes.length; idx++) {
shapes[idx].geometry = shapes[idx].geoCode.toNonIndexed();
m = new THREE.MeshLambertMaterial({
color: shapes[idx].color,
opacity: 0,
transparent: true,
wireframe: true
});
shapes[idx].geometry.center();
shape = new THREE.Mesh(shapes[idx].geometry, m);
scene.add(shape);
}
},
computePointsInside: function(idx) {
shapes[idx].points = this.fillWithPoints(shapes[idx].geometry, MAX_PARTICLES);
},
addText: function() {
return new Promise((resolve, reject) => {
loader.load(typeface, (font) => {
//var x, y, z, index1, index2;
//x = y = z = index1 = index2 = 0;
var m = new THREE.MeshPhongMaterial({color: 0x8F3985, opacity: .8, specular: 0xffffff, shininess: 100});
for (i = 0; i < MAX_PARTICLES; i++) {
g = new THREE.TextGeometry( Math.random() < .5 ? '6' : '9', {
font: font,
size: PARTICLE_SIZE,
height: 0.1
});
var text = new THREE.Mesh(g, m);
text.position.x = 0;
text.position.y = 0;
text.position.z = 0;
text.rotation.x = Math.random() * 2 * Math.PI;
text.rotation.y = Math.random() * 2 * Math.PI;
text.rotation.z = Math.random() * 2 * Math.PI;
text.scale.x = (Math.random() * (0.95 - 0.1) + 0.95).toFixed(4);
text.scale.y = (Math.random() * (0.95 - 0.1) + 0.95).toFixed(4);
text.scale.z = (Math.random() * (0.95 - 0.1) + 0.95).toFixed(4);
//var pointsInsideShape = shapes[0].points.attributes.position.array;
//text.position.x = pointsInsideShape[ index2 ++ ];
//text.position.y = pointsInsideShape[ index2 ++ ];
//text.position.z = pointsInsideShape[ index2 ++ ];
particleGroup.add(text);
}
resolve();
});
})
},
animationSequence: function() {
var x, y, z, index;
x = y = z = index = 0;
const children = particleGroup.children;
var pointsInsideShape = shapes[0].points.attributes.position.array;
for (i = 0; i < MAX_PARTICLES; i++) {
var tl = new TimelineLite();
const child = children[i];
tl.from(child.position, 2, {
x: pointsInsideShape[ index ++ ],
y: pointsInsideShape[ index ++ ],
z: pointsInsideShape[ index ++ ]
})
}
},
animate: function() {
window.requestAnimationFrame(sts.animate);
particleGroup.rotation.y +=0.005;
renderer.render(scene, camera)
},
fillWithPoints: function(geometry, count) {
var ray = new THREE.Ray()
var size = new THREE.Vector3();
geometry.computeBoundingBox();
var bbox = geometry.boundingBox;
var points = [];
var dir = new THREE.Vector3(1, 1, 1).normalize();
for (var i = 0; i < count; i++) {
var p = setRandomVector(bbox.min, bbox.max);
points.push(p);
}
function setRandomVector(min, max){
var v = new THREE.Vector3(
THREE.Math.randFloat(min.x, max.x),
THREE.Math.randFloat(min.y, max.y),
THREE.Math.randFloat(min.z, max.z)
);
if (!isInside(v)){return setRandomVector(min, max);}
return v;
}
function isInside(v){
ray.set(v, dir);
var counter = 0;
var pos = geometry.attributes.position;
var faces = pos.count / 3;
var vA = new THREE.Vector3(), vB = new THREE.Vector3(), vC = new THREE.Vector3();
for(var i = 0; i < faces; i++){
vA.fromBufferAttribute(pos, i * 3 + 0);
vB.fromBufferAttribute(pos, i * 3 + 1);
vC.fromBufferAttribute(pos, i * 3 + 2);
if (ray.intersectTriangle(vA, vB, vC)) counter++;
}
return counter % 2 == 1;
}
return new THREE.BufferGeometry().setFromPoints(points);
},
init: function() {
this.config();
this.initScene();
this.attachEvents();
this.addShapes();
this.computePointsInside(0);
this.computePointsInside(1);
this.computePointsInside(2);
this.addText().then(_ => {
this.animate();
this.animationSequence();
})
}
}
sts.init();
body {
margin: 0;
overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/109/three.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js" type="text/javascript"></script>
I'm back for question two on .points. This time wondering how to change the opacity from 0, to 1 and then back within certain pixel distances from the emitter.
var particleCount = 14,
particles = new THREE.Geometry(),
pMaterial = new THREE.PointsMaterial({
map: new THREE.TextureLoader().load("x.png"),
blending: THREE.multiplyBlending,
flatShading: true,
size: 40,
transparent: true,
depthTest: true,
sizeAttenuation: true,
opacity: 1
});
var particleSystem;
My main confusion is that even though I've given it transparency I can't change the value within the update comp I've made for my emitter.
function update() {
//particleSystem.rotation.y += 0.01;
pCount = particleCount;
while (pCount--) {
particle = particles.vertices[pCount];
(This is where a bunch of validation is for where the points are)
particleSystem.geometry.verticesNeedUpdate = true;
particleSystem.rotation.y += (Math.random()*0.001)
}
Render loop:
renderer.setAnimationLoop(() => {
update();
composer.render(scene, camera);
});
I want to make it fade out and not appear in the scene for 20 or so pixels and then fade in. But I'm not entirely sure on how to change the opacity as particle.opacity += 0.1 won't work.
Edit: I'm also uncertain about Size as I want to do a similar thing with it but from 20 to 40, I could probably base it depending on it's Y cordinate. Anyway; I'm also uncertain how to gradually change that too.
Sorry if this is a obvious answer, duplicate question and any help I get. Any alternate methods of what I've seen is in an alternate structure that I don't understand or in array in which I don't know how to put into what I want.
(Thanks in advance)
The issue is that the opacity and the size is a property of the THREE.PointsMaterial. If the pints should have different sizes it is not sufficient to have a list of different vertices in one THREE.Points. There has to be a list of different THREE.Points with different HREE.PointsMaterials.
Create a list of THREE.Points with different materials:
var texture = new THREE.TextureLoader().load( "..." );
var particleSystemCount = 14;
var particleSystems = [];
for (var i = 0; i < particleSystemCount; ++ i) {
var geometry = new THREE.Geometry();
var pMaterial = new THREE.PointsMaterial({
size: 20,
map: texture,
blending: THREE.AdditiveBlending,
transparent: true,
depthTest: false,
sizeAttenuation: true,
opacity: 0
});
// ...
var points = new THREE.Points(geometry, pMaterial);
scene.add(points);
particleSystems.push(points);
}
So in update the opacity and size can be changed individually:
function update() {
for (var i = 0; i < particleSystems.length; ++ i) {
var points = particleSystems[i];
var material = points.material;
var particle = points.geometry.vertices[0];
// ....
if ( material.size < 40 )
material.size += 0.5;
if ( material.opacity < 1 )
material.opacity += 0.01;
// ....
}
}
var canvas_w = window.innerWidth, canvas_h = window.innerHeight;
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, canvas_w/canvas_h, 1, 1000);
camera.position.set(0, 0, 400);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(canvas_w, window.innerHeight);
document.body.appendChild(renderer.domElement);
window.onresize = function() {
canvas_w = window.innerWidth, canvas_h = window.innerHeight;
renderer.setSize(canvas_w, canvas_h);
camera.aspect = canvas_w/canvas_h;
camera.updateProjectionMatrix();
}
var texture = new THREE.TextureLoader().load("https://threejs.org/examples/textures/sprites/circle.png");
var particleSystemCount = 14;
var particleSystems = [];
for (var i = 0; i < particleSystemCount; ++ i) {
var geometry = new THREE.Geometry();
var pMaterial = new THREE.PointsMaterial({
size: 20,
map: texture,
blending: THREE.AdditiveBlending,
transparent: true,
depthTest: false,
sizeAttenuation: true,
opacity: 0
});
var px = (Math.random() - 0.5) * 100;
var py = (Math.random() - 0.5) * 100 + 200;
var pz = (Math.random() - 0.5) * 100;
var particle = new THREE.Vector3(px, py, pz);
particle.velocity = new THREE.Vector3(0, 0, 0);
geometry.vertices.push(particle);
var points = new THREE.Points(geometry, pMaterial);
scene.add(points);
particleSystems.push(points);
}
function update() {
for (var i = 0; i < particleSystems.length; ++ i) {
var points = particleSystems[i];
var material = points.material;
var particle = points.geometry.vertices[0];
if (particle.y < -200) {
particle.x = (Math.random() - 0.5) * 100;
particle.y = (Math.random() - 0.5) * 100 + 200;
particle.z = (Math.random() - 0.5) * 100;
particle.velocity.y = 0;
material.size = 20;
material.opacity = 0;
}
particle.velocity.y -= Math.random() * .1;
particle.add(particle.velocity);
if ( material.size < 40 )
material.size += 0.25;
if ( material.opacity < 1 )
material.opacity += 0.01;
points.geometry.verticesNeedUpdate = true;
points.rotation.y += (Math.random()*0.001)
}
}
renderer.setAnimationLoop(() => {
update();
renderer.render(scene, camera);
});
body { overflow: hidden; margin: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/99/three.min.js"></script>
I just started exploring three.js and have been trying to adapt a project I found.
I would like to know if it would be possible to have the globe object rotate around it's y-axis with minor additions to the code or whether it has to be rewritten from the ground up.
var canvas = document.querySelector('canvas');
var width = canvas.offsetWidth,
height = canvas.offsetHeight;
var colors = [
new THREE.Color(0xac1122),
new THREE.Color(0x96789f),
new THREE.Color(0x535353)];
var renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio > 1 ? 2 : 1);
renderer.setSize(width, height);
renderer.setClearColor(0xffffff);
var scene = new THREE.Scene();
var raycaster = new THREE.Raycaster();
raycaster.params.Points.threshold = 6;
var camera = new THREE.PerspectiveCamera(50, width / height, 0.1, 2000);
camera.position.set(0, 0, 350);
var galaxy = new THREE.Group();
scene.add(galaxy);
// Create dots
var loader = new THREE.TextureLoader();
loader.crossOrigin = "";
var dotTexture = loader.load("img/dotTexture.png");
var dotsAmount = 3000;
var dotsGeometry = new THREE.Geometry();
var positions = new Float32Array(dotsAmount * 3);
var sizes = new Float32Array(dotsAmount);
var colorsAttribute = new Float32Array(dotsAmount * 3);
for (var i = 0; i < dotsAmount; i++) {
var vector = new THREE.Vector3();
vector.color = Math.floor(Math.random() * colors.length);
vector.theta = Math.random() * Math.PI * 2;
vector.phi =
(1 - Math.sqrt(Math.random())) *
Math.PI /
2 *
(Math.random() > 0.5 ? 1 : -1);
vector.x = Math.cos(vector.theta) * Math.cos(vector.phi);
vector.y = Math.sin(vector.phi);
vector.z = Math.sin(vector.theta) * Math.cos(vector.phi);
vector.multiplyScalar(120 + (Math.random() - 0.5) * 5);
vector.scaleX = 5;
if (Math.random() > 0.5) {
moveDot(vector, i);
}
dotsGeometry.vertices.push(vector);
vector.toArray(positions, i * 3);
colors[vector.color].toArray(colorsAttribute, i*3);
sizes[i] = 5;
}
function moveDot(vector, index) {
var tempVector = vector.clone();
tempVector.multiplyScalar((Math.random() - 0.5) * 0.2 + 1);
TweenMax.to(vector, Math.random() * 3 + 3, {
x: tempVector.x,
y: tempVector.y,
z: tempVector.z,
yoyo: true,
repeat: -1,
delay: -Math.random() * 3,
ease: Power0.easeNone,
onUpdate: function () {
attributePositions.array[index*3] = vector.x;
attributePositions.array[index*3+1] = vector.y;
attributePositions.array[index*3+2] = vector.z;
}
});
}
var bufferWrapGeom = new THREE.BufferGeometry();
var attributePositions = new THREE.BufferAttribute(positions, 3);
bufferWrapGeom.addAttribute('position', attributePositions);
var attributeSizes = new THREE.BufferAttribute(sizes, 1);
bufferWrapGeom.addAttribute('size', attributeSizes);
var attributeColors = new THREE.BufferAttribute(colorsAttribute, 3);
bufferWrapGeom.addAttribute('color', attributeColors);
var shaderMaterial = new THREE.ShaderMaterial({
uniforms: {
texture: {
value: dotTexture
}
},
vertexShader: document.getElementById("wrapVertexShader").textContent,
fragmentShader: document.getElementById("wrapFragmentShader").textContent,
transparent:true
});
var wrap = new THREE.Points(bufferWrapGeom, shaderMaterial);
scene.add(wrap);
// Create white segments
var segmentsGeom = new THREE.Geometry();
var segmentsMat = new THREE.LineBasicMaterial({
color: 0xffffff,
transparent: true,
opacity: 0.3,
vertexColors: THREE.VertexColors
});
for (i = dotsGeometry.vertices.length - 1; i >= 0; i--) {
vector = dotsGeometry.vertices[i];
for (var j = dotsGeometry.vertices.length - 1; j >= 0; j--) {
if (i !== j && vector.distanceTo(dotsGeometry.vertices[j]) < 12) {
segmentsGeom.vertices.push(vector);
segmentsGeom.vertices.push(dotsGeometry.vertices[j]);
segmentsGeom.colors.push(colors[vector.color]);
segmentsGeom.colors.push(colors[vector.color]);
}
}
}
var segments = new THREE.LineSegments(segmentsGeom, segmentsMat);
galaxy.add(segments);
var hovered = [];
var prevHovered = [];
function render(a) {
var i;
dotsGeometry.verticesNeedUpdate = true;
segmentsGeom.verticesNeedUpdate = true;
raycaster.setFromCamera( mouse, camera );
var intersections = raycaster.intersectObjects([wrap]);
hovered = [];
if (intersections.length) {
for(i = 0; i < intersections.length; i++) {
var index = intersections[i].index;
hovered.push(index);
if (prevHovered.indexOf(index) === -1) {
onDotHover(index);
}
}
}
for(i = 0; i < prevHovered.length; i++){
if(hovered.indexOf(prevHovered[i]) === -1){
mouseOut(prevHovered[i]);
}
}
prevHovered = hovered.slice(0);
attributeSizes.needsUpdate = true;
attributePositions.needsUpdate = true;
renderer.render(scene, camera);
}
function onDotHover(index) {
dotsGeometry.vertices[index].tl = new TimelineMax();
dotsGeometry.vertices[index].tl.to(dotsGeometry.vertices[index], 1, {
scaleX: 10,
ease: Elastic.easeOut.config(2, 0.2),
onUpdate: function() {
attributeSizes.array[index] = dotsGeometry.vertices[index].scaleX;
}
});
}
function mouseOut(index) {
dotsGeometry.vertices[index].tl.to(dotsGeometry.vertices[index], 0.4, {
scaleX: 5,
ease: Power2.easeOut,
onUpdate: function() {
attributeSizes.array[index] = dotsGeometry.vertices[index].scaleX;
}
});
}
function onResize() {
canvas.style.width = '';
canvas.style.height = '';
width = canvas.offsetWidth;
height = canvas.offsetHeight;
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height);
}
var mouse = new THREE.Vector2(-100,-100);
function onMouseMove(e) {
var canvasBounding = canvas.getBoundingClientRect();
mouse.x = ((e.clientX - canvasBounding.left) / width) * 2 - 1;
mouse.y = -((e.clientY - canvasBounding.top) / height) * 2 + 1;
}
TweenMax.ticker.addEventListener("tick", render);
window.addEventListener("mousemove", onMouseMove);
var resizeTm;
window.addEventListener("resize", function(){
resizeTm = clearTimeout(resizeTm);
resizeTm = setTimeout(onResize, 200);
});
Codepen here - https://codepen.io/quickwaste/pen/PaGPdw
Thanks.
(A stretch goal would be to have the camera move in response to mouse movement)
Simply add galaxy.rotateY(0.005 * Math.PI); to render(), right before renderer.render(scene, camera) call, like this:
// pulled from the CodePen
function render(a) {
// ... omitted for brevity
prevHovered = hovered.slice(0);
attributeSizes.needsUpdate = true;
attributePositions.needsUpdate = true;
galaxy.rotateY(0.005 * Math.PI);
renderer.render(scene, camera);
}
I used a multiplier of 0.005 to give the globe a nice, lazy spin.
The 'galaxy' object is a THREE.Group, a wrapper of sorts for collections of THREE.Object3D objects. The Object3D has all sorts of nifty functions to help rotate, translate, and transform 3D objects. The rotateY() will spin the model around its local y-axis.
I'm using three.js for creating clickable polygons, when i create polygon in this way
var geo = new THREE.Geometry();
geo.vertices.push(new THREE.Vector3(0.3, 0.3, 0.5));
geo.vertices.push(new THREE.Vector3(0.3, 0.4, 0.5));
geo.vertices.push(new THREE.Vector3(0.4, 0.4, 0.5));
geo.vertices.push(new THREE.Vector3(0.6, 0.35, 0.5));
geo.vertices.push(new THREE.Vector3(0.4, 0.3, 0.5));
for (var face = 0 ; face < 5 - 2; face++) {
// this makes a triangle fan, from the first +Y point around
geo.faces.push(new THREE.Face3(0, face + 1, face + 2));
}
var mesh = new THREE.Mesh(geo, new THREE.MeshBasicMaterial({ color: Math.random() * 0xffffff, opacity: 0.5 }));
geo.computeFaceNormals();
layer.add(mesh);
objects.push(mesh);
it displaying, but the polygon is not clickable.
If I cretae in this way
var geometry = new THREE.CubeGeometry(0.02, 0.02, 0.02);
var object = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ color: Math.random() * 0xffffff, opacity: 0.5 }));
object.position.x = 0.5;
object.position.y = 0.5;
object.position.z = 0.5;
layer.add(object);
objects.push(object);
everything is work fine and cube is clickable, but I need a polygon.
Click Event method
function onDocumentMouseClick(event) {
layerMap.update();
var vector = new THREE.Vector3((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1, 0.5);
var ray = projector.pickingRay(vector, camera);
var intersects = ray.intersectObjects(objects);
if (intersects.length > 0) {
intersects[0].object.material.color.setHex(Math.random() * 0xffffff);
}
}
I have too much different polygons
How to create clickable polygon?
You have two errors.
Your OrthographicCamera constructor args are incorrect -- it is upside down, and the near plane is behind the camera.
//this.camera = new THREE.OrthographicCamera(0, 1, 0, 1, -3000, 3000);
this.camera = new THREE.OrthographicCamera(0, 1, 1, 0, 1, 3000);
The winding order on your geometry is clockwise; it should be counter-clockwise (CCW).
//geo.faces.push(new THREE.Face3(0, face + 1, face + 2));
geo.faces.push(new THREE.Face3(0, face + 2, face + 1));
Tip: debug with the non-minified version of three.js. Also, download a local copy for your use.
three.js r.60
You have to use the intersectTriangle() function of the THREE.Ray class.
If object is your THREE.Mesh loop over your triangles and check for intersections like this:
function onDocumentMouseClick(event)
{
var vector = new THREE.Vector3((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1, 0.5);
var ray = projector.pickingRay(vector, camera);
var currentTriangle;
var currentIntersection, chosenIntersection;
var currentDistance, chosenDistance;
var distanceVector;
chosenIntersection = null;
chosenDistance = Infinity;
var vertices = object.geometry.vertices;
for (var i = 0; i < object.geometry.faces.length; i++)
{
currentTriangle = object.geometry.faces[i];
currentIntersection = ray.intersectTriangle( vertices[ currentTriangle.a ], vertices[ currentTriangle.b ], vertices[ currentTriangle.c ], true );
if( currentIntersection !== null )
{
// The following code checks if a found intersection is closer to the camera than a previous one.
distanceVector.subVectors(currentIntersection, ray.origin);
currentDistance = distanceVector.length();
if( currentDistance < chosenDistance)
{
chosenIntersection = currentIntersection;
chosenDistance = currentDistance;
}
}
}
}