KDTree Distorting Buffer Geometry - three.js

I have a BufferGeometry which has many faces that are colored. When I try applying a KDTree to the positions of the BufferGeometry with
new THREE.TypedArrayUtils.Kdtree(bufferGeometry.attributes.position.array, distanceFunction, 3);
I get my geometry distorted to this:
When it's supposed to be like this:

THREE.TypedArrayUtils.Kdtree sorts the supplied array in-place. You could copy your array and see if it works then.
new THREE.TypedArrayUtils.Kdtree(bufferGeometry.attributes.position.array.slice(0), distanceFunction, 3);
Note the .slice(0);

Instead of subarray(0), I found that you had to initialize a new Float32Array and then set that variable to the array of your other variable.
var newArray = new Float32Array(bufferGeometry.attributes.position.array.length);
newArray.set(bufferGeometry.attributes.position.array);

Related

why three.js updates geometry only when new geometry is defined?

I have a question about updating geometry in three.js using dat.gui.
I expected I could update the geometry by updating the value tied into the geometry by object as below.
gui.add(parameter, 'width')
.min(1)
.max(10)
.step(0.1)
.name('cubeWidth')
.onChange((value) => {
mesh.geometry.dispose();
geometry.parameters.width = parameter.width
console.log(mesh)
})
However, it updates the geometry only when I redefine the geometry like below.
mesh.geometry = new THREE.BoxGeometry(parameter.width, 1, 1);
It's bit weird to me because even when I log the geometry data both shows the updated width value like below.
Why does only the first approach work while the other one not work?
geometry.parameters.width = parameter.width
This has no effect. Parameters are only processed when a geometry is created. The parameters property is in fact read-only.
The solution for your issue is indeed to recreate the geometry with the new set of parameters.

Proper method to move plane vertices locations in React-Three-Fiber

I want to create a plane and set the x,y,z locations for the plane individually. So, in three.js it would be something like this ...
var plane = new THREE.PlaneGeometry(planeSize, planeSize, planeDefinition, planeDefinition);
var plane = new THREE.Mesh(plane, new THREE.MeshBasicMaterial({
color: meshColor,
wireframe: true
}));
for (var i = 0; i < plane.vertices.length; i++) {
plane.vertices[i].z += Math.random() * vertexHeight - vertexHeight;
plane.vertices[i]._myZ = plane.vertices[i].z
}
I don't need to update these vertices locations, so I don't need to use a hook of any kind. Is there a parameter to feed vertices locations directly into the react component? Is there a better way to do this? I tried creating the vertices and then feeding them into the vertices parameter with a primitive, but that requires you to make the faces as well and I could not get that to work.
Thanks
You can't feed vertices directly to the component, you'd have to set that imperatively like in Three.js. You could either get a ref to the geometry component and modify it in useLayoutEffect, or create the geometry inside of something like useMemo or useState and assign it to the geometry prop of the <mesh>. Note that PlaneGeometry is now a BufferGeometry in Three.js r125 so you can't set vertices like you've shown anymore.

Is there a way I can create a Path or Curve to use for TubeGeomety(path,...) from an existing geometry's points/vertices array?

I'm very new to both three.js & to js in general.
1st I select a polyHedron geometry with a dat.gui checkbox
which renders say a tetrahedron. these selections work.
I also have a dat.gui checkbox to either phongfill or wireframe render.
I initially wanted just a wireframe type mesh but not with all of the internal triangles. I found the edgesgeometry() function which draws pretty much what I want(hard edges only). there is however a known issue with linewidth not working in windows anymore. all lines drawn as strokeweight/width 1.
I'd like to use tubeGeometry() to draw tubes of whatever radius as opposed to 1weight lines. I know I'll have to draw something such as a sphere at/over the connection vertices for it to not look ridiculous.
geo = new THREE.TetrahedronBufferGeometry(controls0.Radius,controls0.Detail);
...
egeo = new THREE.EdgesGeometry( geo );
lmat = new THREE.LineBasicMaterial({ color: 0x0099ff, linewidth: 4 });
ph = new THREE.LineSegments( egeo, lmat );
scene.add(ph);
....
playing around in the console I found some geometry/bufferGeomery arrays that are likely the vertices/indices of my selected X-hedron as their sizes change with type(tetra/icosa etc) selection & detail increase/decrease:
//p = dome.geometry.attributes.uv.array;
p = egeo.attributes.position.array
//p = geo.attributes.uv.array
...
var path = new THREE.Curve();
path.getPoint = function (t) {
// trace the arc as t ranges from 0 to 1
var segment = (0 - Math.PI*2) *t;
return new THREE.Vector3( Math.cos(segment), Math.sin(segment), 0);
};
var geomet = new THREE.TubeBufferGeometry( path, 10, 0.2, 12, false );
var mesh = new THREE.Mesh( geomet, mat );
scene.add( mesh );
from above the tubeGeometry() draws fine separately as well but with the "path" made by that curve example. How can I use the vertices from my tetrahedron for example to create that "path" to pass to tubegeometry() ?
maybe a function that creates "segment vectors" from the vertices ?
I think it needs other properties of curve/path as well ?
I'm quite stuck at this point.
ANY Help, suggestions or examples would be greatly appreciated !
thanks.
You can try to create a TubeGeometry for each edge. Generate a LineCurve3 as the input path. Use the vertices of the edge as the start and end vector for the line.
Consider to use something like "triangulated lines" as an alternative in order to visualize the wireframe of a mesh with a linewidth greater than 1. With the next release of three.js(R91) there are new line primitives for this. Demo:
https://rawgit.com/mrdoob/three.js/dev/examples/webgl_lines_fat.html
This approach is much more performant than drawing a bunch of meshes with a TubeGeometry.

Three.js: How to merge two BufferGeometries and keep transforms?

A non-buffergeometry, Geometry.merge(), takes a matrix to apply to the geometry when merging:
.merge ( geometry, matrix, materialIndexOffset )
However, BufferGeometry.merge() does not take a matrix:
.merge ( bufferGeometry, offset )
I have confirmed that merging two BufferGeomtries together does not preserve transforms of the objects being merged.
Here's the kicker: I would like to do this without converting to a Geometry at any step. I would like to do it all in BufferGeometry land.
Is this possible?
We have been doing sich merging of (mostly single objects) to maintain the transformations for JSON Object exporting.
To bake the transformations we do run a small function
getFixedMesh = function(geometry) {
var fixedmesh = null;
switch (geometry.constructor.name) {
case 'BufferGeometry':
var srcmesh = new THREE.Mesh(geometry);
srcmesh.updateMatrix();
return THREE.BufferGeometryUtils.mergeBufferGeometries([srcmesh.geometry]);
default:
var srcmesh = new THREE.Mesh(geometry);
srcmesh.updateMatrix();
fixedmesh = new THREE.Geometry();
fixedmesh.merge(srcmesh.geometry, srcmesh.matrix);
return fixedmesh;
}
}
This works for many arrangements, but still does some errors on LineSegments (and others?)
BufferGeometry has .applyMatrix ( matrix ) method that you can use to 'bake' object transform into vertex data before merging it.

how do I access the attributes of bufferGeometry from an an A-Frame component

I am writing a component that needs to access and modify position, normal, and uv attributes on a model read into A-Frame as an asset. I can get close to accessing the data, but can't quite get there. Using:
document.querySelector('a-entity').object3D.children
seems to give me an array, but trying to access the elements gives me an object with empty children and empty geometry.
My guess is that I'm trying to access the data through the wrong door and while:
console.log(document.querySelector('a-entity').object3D.children);
shows me an array size=1 with a populated Object element
console.log(document.querySelector('a-entity').object3D.children[0]);
gives me the element with the empty geo, etc. What is the right mechanism or syntax to use to get at the data?
There are two three.js classes you'll need to know about here: Geometry and BufferGeometry. The former already has properties for geometry.vertices and geometry.faces (see documentation there). Vertices are an array of THREE.Vertex3 objects, and easy to work with.
If you have a BufferGeometry, then instead you have geometry.attributes.position which is not an array of THREE.Vertex3, but instead contains a flat array of floats, like [x1, y1, z1, x2, y2, ...]. This is more efficient, but harder to modify manually.
If you have BufferGeometry but would rather work with Geometry, then you can convert either way:
var geometry = Geometry.fromBufferGeometry( mesh.geometry );
mesh.geometry = BufferGeometry.fromGeometry( geometry );
An A-Frame specific note, usually you'll get a reference to the mesh by doing el.getObject3D('mesh'). For custom models that might be a nested group, in which case:
el.object3D.traverse(function(node) {
if (node.geometry) { /* ... */ }
});
(three.js r84)

Resources