I'm currently trying to make an animation kinda thing in three.js and for some reason I'm having trouble with scaling objects in the update function, but I don't have issues with opacity, position ect. I'm not sure if I'm just using the wrong commands or whatever but regardless,
I've tried multiple things such as scale, material.scale, geometry.scale, ect. I'm just confused I'm probably just missing obvious things but after googling diffrent solutions and using scale.set(scalex, scaley, 0.000000000001) and just putting a variable in the if states to loop and update the scalex and scaley, but it still doesn't update.
var bpm = 124;
var ring;
var ringgeometry = new THREE.BoxGeometry( 1080/10, 1080/10 , 0 );
var ringmaterial = new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load("RING.png"),
blending: THREE.screenBlending,
transparent: true, opacity: 1
});
var clock = new THREE.Clock;
function update() {
if(clock.elapsedTime < 377){ ticker = clock.elapsedTime * (songBPM / 60);
bpm = Math.round(ticker), console.log(bpm), console.log(sect) };
clock.getDelta();
if(bpm >= 34 && bpm <= 40){
ring = new THREE.Mesh( ringgeometry, ringmaterial);
if(bpm == 34){ scene.add(ring) };
/////////////////////////////////
if(bpm >= 34 && bpm <= 40){ ringgeometry.scale.x += 0.1, ringgeometry.scale.y += 0.1 }
ring.position.set(0, 0, 0);
////////////////////////////////
if(bpm >= (32 + 1) && bpm <= (32 + 10)){ ringmaterial.opacity -= 0.01 };
}
if(bpm == 40 && bpm <= 42){ scene.remove(ring) };
}
renderer.setAnimationLoop(() => {
update();
composer.render(scene, camera);
});
function render(){ composer.render() };
This code only puts the ring into the scene at the default scale, and doesn't remove it from the composition, decrease the opacity or scale it.
I've also been having to find awkward solutions to removing things from scenes because scene.remove() doesn't seem to be working half the time and ends up lagging my scene unless I manually se the opacity to 0 later on.
Thanks in advance and sorry if it's an obvious solution.
threejs.org/docs/index.html#api/en/core/Geometry.scale is the solution.
if(bpm >= 33 && bpm <= (33+6)){
if(bpm == 33){ ring = new THREE.Mesh( ringgeometry, ringmaterial), scene.add(ring) };
if(bpm >= 33 && bpm < 33+9){ ringgeometry.scale(1.01,1.01,1), ringmaterial.opacity -=0.008 }
ring.position.set(0, 0, -70)};
if(bpm == (33+4)){ringgeometry.scale(-10000,-10000,1)};
answer by prisoner849
Related
I am trying to visualize a grand strategy (EU4, CK3, HOI) like map in Three.js. I started creating meshes for every cell. the results are fine (screenshot 1 & 2).
Separate mesh approach - simple land / water differentiation :
Separate mesh approach - random cell color :
however, with a lot of cells, performance becomes an issue (I am getting 15fps with 10k cells).
In order to improve performance I would like to combine all these separate indices & vertex arrays into 2 big arrays, which will then be used to create a single mesh.
I am looping through all my cells to push their indices, vertices & colors into the big arrays like so:
addCellGeometryToMapGeometry(cell) {
let startIndex = this.mapVertices.length;
let cellIndices = cell.indices.length;
let cellVertices = cell.vertices.length;
let color = new THREE.Color( Math.random(), Math.random(), Math.random() );
for (let i = 0; i < cellIndices; i++) {
this.mapIndices.push(startIndex + cell.indices[i]);
}
for (let i = 0; i < cellVertices; i++) {
this.mapVertices.push(cell.vertices[i]);
this.mapColors.push (color);
}
}
I then generate the combined mesh:
generateMapMesh() {
let geometry = new THREE.BufferGeometry();
const material = new THREE.MeshPhongMaterial( {
side: THREE.DoubleSide,
flatShading: true,
vertexColors: true,
shininess: 0
} );
geometry.setIndex( this.mapIndices );
geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( this.mapVertices, 3 ) );
geometry.setAttribute( 'color', new THREE.Float32BufferAttribute( new Float32Array(this.mapColors.length), 3 ) );
for ( let i = 0; i < this.mapColors.length; i ++ ) {
geometry.attributes.color.setXYZ(i, this.mapColors[i].r, this.mapColors[i].g, this.mapColors[i].b);
}
return new THREE.Mesh( geometry, material );
}
Unfortunately the results are underwhelming:
While the data in the combined arrays look okay, only every third cell is rendered. In some cases the indices seem to get mixed up too.
Combined approach - random cell colors :
In other similar topics it is recommended to merge existing meshes. However, I figured that my approach should allow me to better understand what is actually happening & potentially save on performance as well.
Has my code obvious flaws that I cannot see?
Or am I generally on a wrong path, if so, how should it be done instead?
I actually found the issue in my code. wrong:
let startIndex = this.mapVertices.length;
The issue here is that the values in the indices array always reference a vertex (which consists of 3 consecutive array entries in the vertices array). correct:
let startIndex = this.mapVertices.length / 3;
Additionally I should only push one color per vertex instead of one per vertex array entry (= 1 per coordinate) but make sure that the arraylength of the geometry.color attribute stays at it is.
With these 2 changes, the result for the combined mesh looks exactly the same as when creating a separate mesh for every cell. The performance improvement is impressive.
separate meshes:
60 - 65 ms needed to render a frame
144 mb allocated memory
combined mesh:
0 - 1 ms needed to render a frame
58 mb allocated memory
Here are the fixed snippets:
addCellGeometryToMapGeometry(cell) {
let startIndex = this.mapVertices.length / 3;
let cellIndices = cell.indices.length;
let cellVertices = cell.vertices.length;
console.log('Vertex-- maplength: ' + startIndex + ' celllength: ' + cellVertices);
console.log('Indices -- maplength: ' + this.mapIndices.length + ' celllength: ' + cellIndices);
console.log({cell});
let color = new THREE.Color( Math.random(), Math.random(), Math.random() );
for (let i = 0; i < cellIndices; i++) {
this.mapIndices.push(startIndex + cell.indices[i]);
}
for (let i = 0; i < cellVertices; i++) {
this.mapVertices.push(cell.vertices[i]);
if (i % 3 === 0) { this.mapColors.push (color); }
}
}
generateMapMesh() {
let geometry = new THREE.BufferGeometry();
const material = new THREE.MeshPhongMaterial( {
side: THREE.DoubleSide,
flatShading: true,
vertexColors: true,
shininess: 0
} );
geometry.setIndex( this.mapIndices );
geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( this.mapVertices, 3 ) );
geometry.setAttribute( 'color', new THREE.Float32BufferAttribute( new Float32Array(this.mapVertices.length), 3 ) );
for ( let i = 0; i < this.mapColors.length; i ++ ) {
geometry.attributes.color.setXYZ(i, this.mapColors[i].r, this.mapColors[i].g, this.mapColors[i].b);
}
return new THREE.Mesh( geometry, material );
}
I want to create a 1st person 3D game with Three.JS. The controls are W A S D. I have the keys assigned to move forward (W), move backward (S), turn left (A) and turn right (D).
In this code I explain the logic behind the controls. The deltas' porpuse is to inform what direction the camera is looking, so that when it moves forward or backward it moves in that direction. That is what I'm having problems with.
//variables
var pi = Math.PI; //just because I use PI a lot
//controls
var moveForward = false;
var turnLeft = false;
var moveBackward = false;
var turnRight = false;
//direção
var deltaZ = pi;
var deltaX = 0;
var increaseDeltaX = true;
var increaseDeltaZ = false;
/*code adding the camera, the scene and eventListener for the keys. When the keys mentioned are clicked the corresponded var turns true*/
function animate() {
//clicking W
if (moveForward) {
camera.position.z += deltaZ/pi;
camera.position.x -= deltaX/pi;
//I used delta/pi because if delta=pi camera.position.z will increase 1
}
//clicking S
if (moveBackward) {
camera.position.z -= deltaZ/pi;
camera.position.x += deltaX/pi;
}
//clicking D
if (turnRight) {
camera.rotation.y += pi/300; //clicking 300 times will spin 180 degrees
//deltaZ starts in pi and decreasing, it varies between pi and -pi
if (increaseDeltaZ) {
deltaZ += pi / 150; //150= 300/2; pi= 90 degrees = 180/2
} else {
deltaZ -= pi / 150
}
if (deltaZ >= pi) {
increaseDeltaZ = false;
} else if (deltaZ <= -pi) {
increaseDeltaZ = true;
}
//deltaX starts in 0 and increasing, it varies between pi and -pi
if (increaseDeltaX) {
deltaX += pi / 150;
} else {
deltaX -= pi / 150;
}
if (deltaX >= pi) {
increaseDeltaX = false;
} else if (deltaX <= -pi) {
increaseDeltaX = true;
}
}
This code works well. When I turn right and then move forward, the camera moves in the right direction. The problem is when I turn the camera left. I tried to just reverse the calculations but the values just get messed up and the deltas go over pi and the directions become incorrect.
What do I put in the if (left)?
Using three.js
On my home PC with Nvidia GNU, I get about 105 of these in my browser console:
/#/home:1 [.CommandBufferContext]RENDER WARNING: Render count or primcount is 0
But on my intel GNU laptop, I do not. Is this a disagreement with the GNU driver? Or is this something I can fix?
var animationId;
var CAMERA_POSITION = 5000;
function runClouds() {
var speed = 0.8;
cloudsContainer.style.display = 'block';
animationId = requestAnimationFrame( runClouds );
camera.position.y = -75;
if (camera.position.z <= 0) {
window.cancelAnimationFrame(animationId);
camera.position.z = CAMERA_POSITION;
cloudsContainer.style.display = 'none';
return true;
} else if (camera.position.z <= 400) {
speed = 0.1;
} else if (camera.position.z <= 900) {
speed = 0.3;
} else if (camera.position.z <= 2000) {
speed = 0.7;
}
camera.position.z -= 100 * speed;
renderer.render( scene, camera );
}
This warning is thrown when Three.js is trying to render an object that does not exist yet.
Take, for example, a program here you draw a line.
If you add the line to the scene before you add any points, this warning will appear UNTIL the first point is added and a line can be seen.
If you get this, either something is waiting to be added to the scene, or in your case, since you say you get about 105 every time, I assume that an object is added to the scene then due to asychronicity, is actually made in another function after the fact.
Since it is a warning, there isn't much to be afraid about.
I had this problem because I didn't pass a position attribute to a BufferGeometry object. Although my shader overrides position with its own computation, it seems to be a required attribute.
I solved this with:
const positions = new Float32Array(PARTICLE_COUNT * 3);
myGeometry.addAttribute('position', new THREE.BufferAttribute(positions, 3));
The default values of 0 are fine if you're not using the value in your shader.
So I have a heightmap system which works well enough, however since the THREE.js has updated to r60 which removed the Face4 object, I am having issues.
My code is something like this:
this.buildGeometry = function(){
var geo, len, i, f, y;
geo = new THREE.PlaneGeometry(3000, 3000, 128, 128);
geo.dynamic = true;
geo.applyMatrix(new THREE.Matrix4().makeRotationX(-Math.PI / 2));
this.getHeightData('heightmap.png', function (data) {
len = geo.faces.length;
for(i=0;i<len;i++){
f = geo.faces[i];
if( f ){
y = (data[i].r + data[i].g + data[i].b) / 2;
geo.vertices[f.a].y = y;
geo.vertices[f.b].y = y;
geo.vertices[f.c].y = y;
geo.vertices[f.d].y = y;
}
}
geo.computeFaceNormals();
geo.computeCentroids();
mesh = new THREE.Mesh(geo, new THREE.MeshBasicMaterial({color:0xff0000}) );
scene.add(mesh);
});
};
This works well since a pixel represents each face. How is this done now that the faces are all triangulated?
Similarly I use image maps for model positioning as well. Each pixel matches to the respective Face4 and a desired mesh is placed at its centroid. How can this be accomplished now?
I really miss being able to update the library and do not want to be stuck in r59 anymore =[
This approach works fine on the recent versions (tested on r66).
Notice that the genFn returns the height y given current col and row, maxCol and maxRow (for testing purposes, you can of course replace it with a proper array lookup or from a grayscale image... 64x64 determines the mesh resolution and 1x1 the real world dimensions.
var genFn = function(x, y, X, Y) {
var dx = x/X;
var dy = y/Y;
return (Math.sin(dx*15) + Math.cos(dy * 5) ) * 0.05 + 0.025;
};
var geo = new THREE.PlaneGeometry(1, 1, 64, 64);
geo.applyMatrix(new THREE.Matrix4().makeRotationX(-Math.PI / 2));
var iz, ix,
gridZ1 = geo.widthSegments +1,
gridX1 = geo.heightSegments+1;
for (iz = 0; iz < gridZ1; ++iz) {
for (ix = 0; ix < gridX1; ++ix) {
geo.vertices[ ix + gridX1*iz ].y = genFn(ix, iz, gridX1, gridZ1);
}
}
geo.computeFaceNormals();
geo.computeVertexNormals();
geo.computeCentroids();
var mesh = new THREE.Mesh(
geo,
mtl
);
scene.add(mesh);
I'm having a little trouble generating/displaying a terrain using Three.JS without major FPS drops. Here's the code I wrote to create each block and set the correct position:
var TO_METERS = 10;
var testOb = [];
var blockGeometry = new THREE.CubeGeometry(TO_METERS, TO_METERS, TO_METERS);
var blockMat = new THREE.MeshLambertMaterial({color: 0xFFFFFF, wrapAround: true, side: THREE.FrontSide, shading: THREE.FlatShading});
function loadChunk(startX, startY, startZ) {
var yVar = 0;
var zVar = 0;
var blockCo = 0;
var combinedGeometry = new THREE.CubeGeometry(0, 0, 0);
for (var x = 0; x <= 4999; x++) {
testOb[x] = new THREE.Mesh();
testOb[x].geometry = blockGeometry;
if (blockCo == 10) {
blockCo = 0;
if (zVar == 90) {
yVar += TO_METERS;
zVar = 0;
}
else {
zVar += TO_METERS;
}
}
testOb[x].position.x = (blockCo * TO_METERS) + startX;
testOb[x].position.y = (yVar - 500) + startY;
testOb[x].position.z = zVar + startZ;
testOb[x].castShadow = true;
blockCo++;
THREE.GeometryUtils.merge(combinedGeometry, testOb[x]);
}
var cMesh = new Physijs.BoxMesh(combinedGeometry, blockMat, 0);
scene.add(cMesh);
}
Basically it creates each block, sets the position and merges them together using THREE.GeometryUtils.merge to make up a "chunk" (a rectangle) MineCraft style.
I'm pretty sure the large number of individual blocks that make up each chunk is causing the low FPS. With only 10 chunks the FPS is fine. If I add any more the FPS drops drastically.
One thought I had was to use a WebWorker to do the processing, but of cause that isn't possible as I can't add the chunks or even use Three.JS within it. That also would only help the load time, not the FPS problem I'm having.
If anyone has any ideas how I would go about fixing this problem, I would really appreciate it. :) Maybe it would be possible to hide blocks which the camera can't see? Or I might just totally be doing it the wrong way. Thanks!