How to use geomertryUtils merge in three js? - three.js

I'm using old version of three js because some library compatible problem. There's no Geometry.merge function in it, all i could use is GeometryUtils.merge.
What i am trying to do is to have a mesh, and then merge other mesh into it(i mean merge their geometries) later in the animation loop.
The result is funny, the console.log indicates that the vertices have been added into the group geometry. I checked their positions, they seem right.
However, they seemly don't show up in the scene ( i can't see them!)
Notice that i called add() once before the animation function, and it works!
It just does not work in the animation loop. So weird!!!
var group = new THREE.Geometry();
var mmm = new THREE.Mesh(group, new THREE.MeshNormalMaterial());
scene.add(mmm);
var counter = 0;
function add(){
for(var i = counter + 1; i < counter + 2 ; i++){
var geo = new THREE.BoxGeometry(0.1, 0.1, 0.1);
var m = new THREE.Mesh(geo, material);
m.position.set(i, i, 0);
console.log(i)
group.verticesNeedUpdate = true;
THREE.GeometryUtils.merge(group, m);
}
counter ++;
console.log(group.vertices.length);
}
add();
var frameCount = 0;
function animate(){
requestAnimationFrame(animate);
if(frameCount !== 0 && frameCount % 60 === 0){
add();
}
frameCount++;
renderer.render( scene, camera );
}
animate();

Related

Animating Three.js vertices

I'm trying to animate individual vertices from a collada model.
The vertices ARE animating fine once but then they don't animate anymore.
1: Load my collada
// name obj
var orgVerts = false;
var desVerts = false;
var material = new THREE.MeshBasicMaterial({ color:0x1e5679, wireframe:true });
var loader = new THREE.ColladaLoader();
var nameModel = false;
loader.options.convertUpAxis = true;
loader.load( '3d/name.dae', function ( collada ) {
nameModel = collada.scene.children[0].children[0];
nameModel.material = material;
nameModel.geometry.dynamic = true;
nameModel.position.set(0,0,0);//x,z,y- if you think in blender dimensions ;)
nameModel.scale.x = .0035;
nameModel.scale.y = .0035;
nameModel.scale.z = .0035;
scene.add(nameModel);
orgVerts = nameModel.geometry.vertices; // make a backup of all verts
genVerts(); // create a new array of random verts
});
2: in my render function
var render = function () {
requestAnimationFrame(render);
// do stuff
if(nameModel){
if(window.globalCurrentSlide != 0){
for(var r=0; r < nameModel.geometry.vertices.length; r++){
var vert = desVerts[r]; // loop through all the destination verts
nameModel.geometry.vertices[r].x = nameModel.geometry.vertices[r].x - (nameModel.geometry.vertices[r].x - vert.x)/20;
nameModel.geometry.vertices[r].y = nameModel.geometry.vertices[r].y - (nameModel.geometry.vertices[r].y - vert.y)/20;
nameModel.geometry.vertices[r].z = nameModel.geometry.vertices[r].z - (nameModel.geometry.vertices[r].z - vert.z)/20;
}
}else{
for(var t=0; t < nameModel.geometry.vertices.length; t++){
var vert2 = orgVerts[t]; // loop through all the original verts
nameModel.geometry.vertices[t].x = nameModel.geometry.vertices[t].x - (nameModel.geometry.vertices[t].x - vert2.x)/20;
nameModel.geometry.vertices[t].y = nameModel.geometry.vertices[t].y - (nameModel.geometry.vertices[t].y - vert2.y)/20;
nameModel.geometry.vertices[t].z = nameModel.geometry.vertices[t].z - (nameModel.geometry.vertices[t].z - vert2.z)/20;
}
}
nameModel.geometry.verticesNeedUpdate = true;
nameModel.rotation.y += .005;
}
renderer.render( scene, camera );
}
window.globalCurrentSlide is set to 0 to start with and everything is fine. if I change window.globalCurrentSlide to 1, all the vertices animate correctly... BUT when I change window.globalCurrentSlide back to 0 the vertices don't animate back to their original positions. I've debugged heaps and can 100% say that BOTH desVerts and orgVerts don't change and they are correct. Any ideas? It's driving me nuts.
PS: I know the code could be condensed, I'm just playing ATM
The answer was that my orgVerts was just a reference NOT a clone. use:
var geometry = nameModel.geometry.clone();
orgVerts = geometry.vertices;

Three.js, Raycaster, array of rays?

I have two rays, one highlights a mesh when is in the center of camera and the other one highlights a mesh on click. JS Bin:
// ...
raycasterClick = new THREE.Raycaster();
raycasterCenter = new THREE.Raycaster();
// ...
function animate() {
requestAnimationFrame(animate);
// ...
// clear intersected
for (var i = 0; i < INTERSECTED.length; i++) {
INTERSECTED[i].material.emissive.setHex(INTERSECTED[i].currentHex);
INTERSECTED.splice(i, 1);
}
// click
raycasterClick.setFromCamera(mouse, camera);
var intersects = raycasterClick.intersectObjects(scene.children);
for (var i = 0; clickedIn && i < intersects.length; i++) {
var length = INTERSECTED.push(intersects[0].object) - 1;
INTERSECTED.currentHex = INTERSECTED[length].material.emissive.getHex();
INTERSECTED[length].material.emissive.setHex(0xff0000);
}
// center
raycasterCenter.setFromCamera(center, camera);
var intersects = raycasterCenter.intersectObjects(scene.children);
for (var i = 0; i < intersects.length; i++) {
var length = INTERSECTED.push(intersects[0].object) - 1;
INTERSECTED.currentHex = INTERSECTED[length].material.emissive.getHex();
INTERSECTED[length].material.emissive.setHex(0xff0000);
if(intersects[0].distance<=2&&controls.moveZ==-1)controls.moveZ=0;
}
// ...
}
I was wondering if I have to call a new THREE.Raycaster() for each ray like in my example above or there is a way to only call it once by making an array of rays. With an array I would have to loop only once through all the scene.children. I'm thinking of resources.
How to create an array of rays in Three.js?

How to efficiently draw lots of objects using Three.js

I am new to WebGL and Three.js. I am trying to visualize a large grid of circles changing colors at once.
As I increase the number of instances, it gets noticeably slower, where it takes seconds to update. What are some suggestions for improving my code? Can I update 4000 circles at once?
Here is my existing implementation:
<html>
<head>
<title>My first Three.js app</title>
<style></style>
</head>
<body>
<script src="./three.js"></script>
<script>
var ROWS = 40
var COLS = 100
var SEGMENTS = 10;
var windowWidth = window.innerWidth, windowHeight = window.innerHeight;
var camera, scene, renderer;
var group, text, plane;
function init() {
// create and append container/canvas
container = document.createElement( 'div' );
document.body.appendChild( container );
// create camera
camera = new THREE.PerspectiveCamera(100, windowWidth / windowHeight, 0.1, 1000 );
// set position of camera
camera.position.z = 500;
camera.position.x = windowWidth/2
camera.position.y = windowHeight/2
// Create a scene
scene = new THREE.Scene();
renderer = new THREE.CanvasRenderer();
renderer.setClearColor( 0xf0f0f0 );
renderer.setSize( windowWidth, windowHeight );
renderer.sortElements = false;
container.appendChild( renderer.domElement );
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
}
function addCircle(color, x, y, z, s , radius) {
var geometry = new THREE.CircleGeometry(radius, SEGMENTS, SEGMENTS)
var material = new THREE.MeshBasicMaterial( { color: color, overdraw: true } );
var mesh = new THREE.Mesh( geometry, material );
mesh.position.set( x, y, z );
mesh.scale.set( s, s, s );
scene.add( mesh );
}
function toHex(d) {
var valueStr = d.toString(16);
valueStr = valueStr.length < 2 ? "0"+valueStr : valueStr;
var fillColor = "0x00" + valueStr + "00";
return parseInt(fillColor);
}
function drawData(data) {
var rows = data.length;
var cols = data[0].length;
distanceBetweenCircles = Math.min(windowWidth/(cols), windowHeight/(rows));
var radius = distanceBetweenCircles/2.0
for(var i = 0; i < data.length; i++) {
for (var j = 0; j < data[0].length; j++) {
var color = toHex(data[i][j])
var x = distanceBetweenCircles*j - radius
var y = distanceBetweenCircles*i - radius
addCircle( color, x, y, 0, 1 , radius-3);
}
}
}
function newData(){
var newData = []
for (var i = 0; i < ROWS; i++) {
var row = [];
for (var j = 0; j < COLS; j ++) {
row.push(Math.floor(Math.random()*255));
}
newData.push(row);
}
return newData;
}
function onDocumentMouseDown ( event ) {
event.preventDefault();
// Update circles
var randomData = newData()
drawData(randomData);
}
var render = function() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
init();
render();
</script>
</body>
</html>
To add a CircleGeometry object to a scene requires the circle to be drawn. Adding an image/texture of a circle to a scene requires the circle to be printed, so to speak.
Multiply that by 4000 and the drawing becomes quite expensive.
It would be faster to maintain an array of the meshes and update their properties, rather than creating a new set of geometries, materials and meshes every mouse click.
Memory management is extremely important in software design. There is a cost to every variable you introduce, especially those who's instantiation invokes a cascade of allocations behind some API call. This is the down side of using layerings like THREE which hide complexity but also hide ramifications of using their calls. Short of avoiding THREE and doing all the WebGL plumbing yourself (which is always a good first step before ignoring the plumbing and just using a shim like THREE), do some homework to identify what is getting created as you make calls to any API, like THREE. Rip out of inner loops variable creation for objects which should be reused across calls. To your question, yes you can easily update 4000 circles across each animation event loop time slice once your architecture is carefully thought through especially if you use shaders to craft your objects and avoid such computation back in the CPU
For pure speed I suggest you learn graphics by writing OpenGL/WebGL by hand instead of the higher level abstraction library Three.js ... the price of ease of use too often is higher computational load of unnecessary logic which can be cut out if written by hand
Here is a WebGL toy I built which has no Three.js ... it does real-time updates to geometry of 10's of thousands of objects as well as rendering audio using Web Audio API
https://github.com/scottstensland/webgl-3d-animation

three.js animation logic for spinning cuboids

I'm trying to create a cuboid from 5 cuboids one of top of the other - that rotates 180 CW degrees every two seconds.
It's kinda working but I'm not getting smooth results... I thought a good strategy would be that during the rotating period- the z-position of the top-left and top-right vertices of a single face will be compared- if they are the same then it's time to stop the rotating period.
The problem is that the difference between the vertices is never zero.. it's very close to zero... so I check if it's in a margin of 0.1 and thus I have a problem with the beginning of a rotation that is jammed because sometimes it's less than 0.1. Also sometimes the rotations continue when they should stop because the difference is not less than 0.1.
var spinningPeriod = false, counter = 0, lastTime = 0;
function animate(){
counter++;
var time = (new Date()).getTime();
var timeDiff = time - lastTime;
var angleChange = 0.2 * timeDiff * 2 * Math.PI / 1000;
if (counter%200==0 && counter > 0) {
spinningPeriod = true;
}
if (spinningPeriod) {
var v1 = cubes[0].geometry.vertices[0].clone();
var v2 = cubes[0].geometry.vertices[3].clone();
cubes[0].updateMatrixWorld();
cubes[0].localToWorld(v1);
cubes[0].localToWorld(v2);
if (Math.abs(v1.x - v2.x) < 0.1) {
spinningPeriod = false;
}
for (var ii =0; ii<5; ++ii) {
cubes[ii].rotation.y += angleChange;
}
}
lastTime = time;
renderer.render(scene, camera);
// request new frame
requestAnimationFrame(function(){
animate();
});
}
// renderer
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// camera
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.z = 500;
// scene
var scene = new THREE.Scene();
// cube
var cubes = new Array();
var currentHeight = -150;
for (var ii =0; ii<5; ++ii) {
cubes[ii] = new THREE.Mesh(new THREE.CubeGeometry(400, 50, 20));
currentHeight += 50;
cubes[ii].position.y = currentHeight;
cubes[ii].overdraw = true;
scene.add(cubes[ii]);
}
// start animation
animate();
I agree, looking at the position is probably not the best way to do it. If you're concerned with the angle, then the angle should be your focus. Basically you want to have your test be based on the spin vs a max spin - when you're more than that, stop and adjust as needed (in this case, it was sometimes > 1 with the amount of angleChange, so I just reset it to 1).
Instead of your animate function above, try this one - it looks like it works pretty reliably for me.
var spinningPeriod = false, counter = 0, lastTime = 0;
var spinAngle = 0, spinMax = 1 * Math.PI;
function animate(){
counter++;
var time = (new Date()).getTime(),
timeDiff = time - lastTime,
speed = 0.2 * 2 * Math.PI / 1000,
angleChange = speed * timeDiff;
if (counter%200==0 && counter > 0) {
spinningPeriod = true;
spinAngle = 0;
}
console.log(counter, spinningPeriod, cubes[0].rotation.y / Math.PI);
if (spinningPeriod) {
spinAngle += angleChange;
if (spinAngle > spinMax) {
spinningPeriod = false;
spinAngle = spinMax;
}
for (var ii =0; ii<5; ++ii) {
cubes[ii].rotation.y = spinAngle;
}
}
lastTime = time;
renderer.render(scene, camera);
// request new frame
requestAnimationFrame(function(){
animate();
});
}

Accessing single particles in THREE.js Particle System

I really tried every example, searched the web for hours but I can't seem to get it working!
So I simply tried to implement a little particle system simulating falling snow, just like this: http://www.aerotwist.com/tutorials/creating-particles-with-three-js/
But I only can access it in whole. Meaning I can rotate it as such but as soon as I try to iterate over it's vertices, the whole animation is getting the hiccups! I would really appreciate some help here!
-
Here are the key parts:
-> Setting up the particle system:
var partikelGeo = new THREE.Geometry();
var partikelMaterial = new THREE.ParticleBasicMaterial({
color:0xffffff,
size: 10,
map: THREE.ImageUtils.loadTexture('snowflake2.png'),
blending: THREE.AdditiveBlending,
transparent:true
});
var partikelAnzahl = 3500;
for (var p = 0; p < partikelAnzahl; p++) {
var pX = Math.random() * 1000 -500;
var pY = Math.random() * 1000 -500;
var pZ = Math.random() * 1000 -500;
var partikel = new THREE.Vertex(new THREE.Vector3(pX,pY,pZ));
partikel.velocity = new THREE.Vector3(0,-Math.random(),0);
partikelGeo.vertices.push(partikel);
}
var partikelSystem = new THREE.ParticleSystem(partikelGeo, partikelMaterial);
partikelSystem.sortParticles = true;
scene.add(partikelSystem);
-> Rendering & Animation on mouseclick
var frame = 0;
function animate(){
// request new frame
requestAnimationFrame(function(){
animate();
});
// render
render();
}
animate();
var check = 0;
onmousedown = function(){
if (check) {
check = 0;
}else{
check = 1;
}
}
function render() {
if (check) {
clickDo();
}
camera.lookAt(new THREE.Vector3(0,0,0));
renderer.render(scene,camera);
}
function clickDo() {
frame++;
partikelSystem.rotation.y += 0.01;
var pCount = partikelAnzahl;
while(pCount--) {
// get the particle
var particle =
partikelGeo.vertices[pCount];
// check if we need to reset
if(particle.position.y < -200) {
particle.position.y = 200;
particle.velocity.y = 0;
}
// update the velocity with
// a splat of randomniz
particle.velocity.y -=
Math.random() * .1;
// and the position
particle.position.addSelf(
particle.velocity);
}
// flag to the particle system
// that we've changed its vertices.
partikelSystem.
geometry.
__dirtyVertices = true;
}
Rah
Your code looks good to me. I would just suggest to try not sorting your particles as you use an additive blending:
partikelSystem.sortParticles = false;

Resources