Three.js r125 BufferGeometry `vertices` does not exist - three.js

I'm working on updating Three.js and I find that when I upgrade to r125, attempts to set vertices on BufferGeometry fail due to the method missing. It also appears to have removed verticesNeedUpdate. The migration guide does not appear to warn about this and the changelog doesn't seem to address it from what I can see.
I unfortunately didn't write the original code so I'm unsure of how to resolve it. The code looks like this:
this.geometry.vertices[0].x = this.geometry.vertices[2].x = -this.canvas.width / 2;
this.geometry.vertices[1].x = this.geometry.vertices[3].x = this.canvas.width / 2;
this.geometry.vertices[0].y = this.geometry.vertices[1].y = this.canvas.height / 2;
this.geometry.vertices[2].y = this.geometry.vertices[3].y = -this.canvas.height / 2;
this.geometry.verticesNeedUpdate = true;
Update using Don's Answer Below
After applying Don's suggested change, we wind up with this:
const negativeWidth = -this.canvas.width / 2;
const positiveWidth = this.canvas.width / 2;
const positiveHeight = this.canvas.height / 2;
const negativeHeight = -this.canvas.height / 2;
this.geometry.attributes.position.setXY(0, negativeWidth, positiveHeight);
this.geometry.attributes.position.setXY(1, positiveWidth, positiveHeight);
this.geometry.attributes.position.setXY(2, negativeWidth, negativeHeight);
this.geometry.attributes.position.setXY(3, positiveWidth, negativeHeight);
this.geometry.attributes.position.needsUpdate = true;

The first changelog entry for three.js r125 is the relevant one:
Geometry has been removed from the core. It is now located in examples/jsm/deprecated/Geometry.js.
The THREE.Geometry class has been been deprecated for a while, but some older code and examples outside of the project repository still refer to it. The recommended replacement is THREE.BufferGeometry, which is more performant. The BufferGeometry class does not have a .vertices property, so that's probably the cause of the particular error you're seeing. Instead, you can update vertices like this:
geometry.attributes.position.setXYZ( index, x, y, z );
geometry.attributes.position.needsUpdate = true;

Related

Three.js - repositioning vertices in a 'particle' mesh

I have a basic three.js game working and I'd like to add particles. I've been searching online, including multiple questions here, and the closest I've come to getting a 'particle system' working is using a THREE.BufferGeometry, a THREE.BufferAttribute and a THREE.Points mesh. I set it up like this:
const particleMaterial = new THREE.PointsMaterial( { size: 10, map: particleTexture, blending: THREE.AdditiveBlending, transparent: true } );
const particlesGeometry = new THREE.BufferGeometry;
const particlesCount = 300;
const posArray = new Float32Array(particlesCount * 3);
for (let i = 0; i < particlesCount; i++) {
posArray[i] = Math.random() * 10;
}
const particleBufferAttribute = new THREE.BufferAttribute(posArray, 3);
particlesGeometry.setAttribute( 'position', particleBufferAttribute );
const particlesMesh = new THREE.Points(particlesGeometry, particleMaterial);
particlesMesh.counter = 0;
scene.add(particlesMesh);
This part works and displays the particles fine, at their initial positions, but of course I'd like to move them.
I have tried all manner of things, in my 'animate' function, but I am not happening upon the right combination. I'd like to move particles, ideally one vertex per frame.
The current thing I'm doing in the animate function - which does not work! - is this:
particleBufferAttribute.setXYZ( particlesMesh.counter, objects[0].position.x, objects[0].position.y, objects[0].position.z );
particlesGeometry.setAttribute( 'position', particleBufferAttribute );
//posArray[particlesMesh.counter] = objects[0].position;
particlesMesh.counter ++;
if (particlesMesh.counter > particlesCount) {
particlesMesh.counter = 0;
}
If anyone has any pointers about how to move Points mesh vertices, that would be great.
Alternatively, if this is not at all the right approach, please let me know.
I did find Stemkoski's ShaderParticleEngine, but I could not find any information about how to make it work (the docs are very minimal and do not seem to include examples).
You don't need to re-set the attribute, but you do need to tell the renderer that the attribute has changed.
particleBufferAttribute.setXYZ( particlesMesh.counter, objects[0].position.x, objects[0].position.y, objects[0].position.z );
particleBufferAttribute.needsUpdate = true; // This is the kicker!
By setting needsUpdate to true, the renderer knows to re-upload that attribute to the GPU.
This might not be concern for you, but just know that moving particles in this way is expensive, because you re-upload the position attribute every single frame, which includes all the position data for every particle you aren't moving.

adding perlin noise to THREE.js r121

I have been struggling with finding a good tutorial to add perlin noise to a newer version of THREE.js. I have found many tutorials and examples but they all seem to break when I add r121 into the mix.
I've tried a great example from Codepen using a script from jeromeetienne.github.io
I've also tried this guys tutorial and file http://www.stephanbaker.com/post/perlinterrain/
I've tried several others and no luck. I am convinced it's due to THREE versions being used. Most of what I can find use an old version of THREE.js. I have forked what others have, and it works with the old version they are using but not the new one.
//land
let landGeo = new THREE.PlaneGeometry(500,500, 50, 50);
let landMat = new THREE.MeshStandardMaterial({
color:'green'
})
land = new THREE.Mesh(landGeo, landMat);
scene.add(land);
noise.seed(Math.random());
for(let i=1;i<100;i++) {
for(let j=1;j<100;j++) {
var value = noise.simplex2(i / 100, j / 100);
// ... or noise.simplex3 and noise.perlin3:
// var value = noise.simplex3(i / 100, j / 100, clock);
landGeo[i][j].r = Math.abs(value) * 256;
}
}
So does anyone know how can get some version of perlin noise working? I am trying to creat terrain.
https://codepen.io/jfirestorm44/pen/mdEEwqb?editors=0010
Thank you
Did you check the browser console errors? It should be the first thing you check.
I see the error Cannot read property '1' of undefined.
Which tells me that something is wrong with the line landGeo[i][j].r = Math.abs(value) * 256;
The vertices are in a single dimension array and are vector objects. I am pretty sure it should be:
landGeo.vertices[i+j].z = Math.abs(value) * 256;.
Also, I am not sure you are calling the noise function with the correct parameters, though I could be wrong and you may be wanting it like it is.
var value = noise.simplex2(i / 100, j / 100);
Also, you are starting your for loops at 1, for(let i=1;i<100;i++) {, I am pretty sure you want to start them at 0. for(let i=0;i<100;i++) {
There is always the option of doing the displacement in a shader like this 3d example, only in 2d

calling object3D children for unique styles / animations

I'm wondering how I would go about calling the individual children of cube (mesh0, mesh1) so I'm able to set different styles / animations to them both.
AFRAME.registerComponent('multi_box', {
schema: {},
update: function() {
for (var i = 0; i < 2; i++) {
var material = new THREE.MeshBasicMaterial({color: "blue"});
var geometry = new THREE.BoxGeometry(1, 1, 1);
var cube = new THREE.Mesh(geometry, material);
cube.position.x = i == 0 ? -1 : 1;
cube.position.y = 0.5;
cube.position.z = -5;
this.el.setObject3D("mesh"+i, cube); //unique name for each object
}
console.log(this.el.object3DMap)
}
});
Codepen link: https://codepen.io/ubermario/pen/wrwjVG
I can console.log them both and see that they are unique objects to each other but I'm having trouble calling them:
var meshtest = this.el.getObject3D('mesh0')
console.log(meshtest.position)
I'v tried this method but with no luck: aframe get object3d children
Any help is appreciated :)
Instancing
In your for cycle, you create
a new geometry instance
a new material instance
a new mesh that connect the previous two
Each new keyword creates a unique instance that is independent of the others. In your case mesh0 and mesh1 are fully independent of each other. If you change any property of an instance, like for example material color or position, the other(s) will not be affected by that. In fact you do that by assigning a different x position to each cube.
Storage
Your component holds a map of 3D objects. Each is identified by a unique name. You generate this name by concatenating the prefix mesh with the iteration number (value of i).
You can later access the cubes just the same way you created them. Either by name
this.el.getObject3D('mesh0')
this.el.getObject3D('mesh1')
//etc.
or by index
this.el.object3D.children[0]
this.el.object3D.children[1]
and you can manipulate them further. For example you can put his on Ln19 in your Codepen:
this.el.getObject3D('mesh0').position.y = 2;
this.el.object3D.children[1].position.z = -3;
Just for completeness: If you would omit the +i at the end, you would overwrite the same key again and again, so mesh would reference just the last cube and others would get lost.
Changing properties
Three.js has a nice API, but in javascript you always need to think what happens behind the scenes. You will see that while learning new stuff. For example:
this.el.object3D.children[1].position.z = -3;
this.el.object3D.children[1].material.color.set('#ffff00');
this.el.object3D.children[1].material.color = [1, 1, 0];
As you can see the position can be changed directly, but color needs a setter sometimes. Things get more complicated with vectors where you need to watch which methods change the current instance and which produce a new one. You could easily forget to clone it. Say you had:
var pos = new THREE.Vector3(-1, 0.5, -5)
for (var i = 0; i < 2; i++) {
var material = new THREE.MeshBasicMaterial({color: "blue"});
var geometry = new THREE.BoxGeometry(1, 1, 1);
var cube = new THREE.Mesh(geometry, material);
//wrong, since it will assign the object reference,
//i.e. always the same object.
//Thus, the cubes will be at one position in the end
cube.position = pos;
//right
cube.pos = pos.clone();
pos.x += 1;
this.el.setObject3D("mesh"+i, cube); //unique name for each object
}
The difference is a bit more moderate topic on reference and value types. You will find many tutorials for that, for example this small gist I just found.
I hope this was helpful and wish you good progress in learning!

object.updateWorldMatrix is not a function

As documentation of Three.js said, after changing the position of the camera, we must call the updateProjectionMatrix() method.
I'm doing the same. But it gives me an error like this:
TypeError: object.updateWorldMatrix is not a function
at Box3.expandByObject (three.module.js:6329)
at Box3.setFromObject (three.module.js:6233)
at index.js:66
at GLTFLoader.js:147
at GLTFLoader.js:1639
My goal is to put my loaded GLTF object, in the center of the screen.
And this is the code I'm using for it:
this.gltfLoader.load("/corolla.gltf", (object) => {
const box = new THREE.Box3().setFromObject(object);
const size = box.getSize(new THREE.Vector3()).length();
const center = box.getCenter(new THREE.Vector3());
// reset OrbitControl
this.controls.reset();
object.position.x += (object.position.x - center.x);
object.position.y += (object.position.y - center.y);
object.position.z += (object.position.z - center.z);
this.controls.maxDistance = size * 10;
this.camera.near = size / 100;
this.camera.far = size * 100;
this.camera.updateProjectionMatrix();
this.camera.position.copy(center);
this.camera.position.x += size / 2.0;
this.camera.position.y += size / 5.0;
this.camera.position.z += size / 2.0;
this.camera.lookAt(center);
this.gltf = object.scene;
this.scene.add(this.gltf);
},
this.manageLoading,
this.gltfLoadErr);
For the record, I'm using the last version of Three.js, it's 0.110.0;
It looks like the errors is occurring here:
const box = new THREE.Box3().setFromObject(object);
Where object is the object coming back from the call to GLTFLoader.load.
According to the docs, object is not an Object3D (which is what Box3.setFromObject expects).
Instead, object is a JSON structure containing information about the GLTF data. The three.js example suggests object.scene would be a renderable entity (if it exists).
Take a look at the code for the GLTFLoader example, here. Compare it against your implementation. You can also debug your code to see exactly what is object contains. Once you have a handle on that, if you're still having problems, come back and ask more questions!
Short solution:
var hollowCylinderGeom = new THREE.LatheBufferGeometry([
new THREE.Vector2(1, 0),
new THREE.Vector2(2, 0),
new THREE.Vector2(2, 2),
new THREE.Vector2(1, 2),
new THREE.Vector2(1, 0)
], 90).toNonIndexed();
var mesh = new THREE.Mesh(hollowCylinderGeom);
console.log(new THREE.Box3().setFromObject(mesh));

Blending artifacts when rendering polygon hair with Three.js

Trying to render polygon hair with transparency I get this artifact:
What I've checked so far:
renderer.sortObjects is true.
The hair's material.side is THREE.DoubleSide.
Set material.alphaTest=0.5 - reduced the problem but some artifacts are still visible.
How do I debug this?
What solved the problem was rendering the hair twice, first the back side and then the front side:
var hairMesh1 = obj;
var hairMaterial1 = hairMesh1.material;
hairMaterial1.opacity = 1.0;
hairMaterial1.transparent = true;
hairMaterial1.side = THREE.BackSide;
hairMaterial1.depthWrite = false;
var hairMesh2 = hairMesh1.clone();
var hairMaterial2 = hairMesh2.material = hairMesh2.material.clone();
hairMaterial2.side = THREE.FrontSide;
var hairObj2 = new THREE.Object3D();
hairObj2.add(hairMesh2)
hairObj2.renderOrder = 1;
model.add(hairObj1);
model.add(hairObj2);
This is explained in this answer.
Another thing I tried was like here - I set
material.alphaTest = 0.5; // between 0 and 1
which reduced problem (still noticeable artifacts seen). An explanation can be found here.

Resources