Strange result with ThreeCSG and subtract - javascript

I'm learning three.js and for a project i need to create an inersection between a cylinder and a sphere.
Here is the interesting part of the code :
var sphere_mesh_3 = createSphereMesh(rayon_1, 145, color);//,-2,2,-2,2);
sphere_mesh_3.position.z = 6;
//scene.add(sphere_mesh_3);
var sphere_mesh_4 = createSphereMesh(rayon_2, 145, color);//,-2,2,-2,2);
sphere_mesh_4.position.z = 7.5;
//scene.add(sphere_mesh_4);
function getZmaxRelSurfaceAspherique(mesh)
{
var zMax = 0;
for(var i = 0; i < mesh.geometry.vertices.length; i++)
{
var vec = mesh.geometry.vertices[i].clone();
var x = vec.x;
var y = vec.y;
var z = vec.z;
var abs_z = Math.abs(z);
if(x!== 0 && y!==0 && Math.abs(z) !== 1.0)
{
zMax = abs_z;
}
}
return zMax;
}
var min_sph_2 = getZmaxRelSurfaceAspherique(sphere_mesh_3);
var max_sph_2 = getZmaxRelSurfaceAspherique(sphere_mesh_4);
var pos_z_1 = 6 + min_sph_2;
var pos_z_2 = 7.5 + max_sph_2;
var cylindre_sph = cylindreJointure(0,0,pos_z_2,0,0,pos_z_1,Math.abs(rayon_1*80),0xffff00);
var distance = Math.abs(pos_z_1 - pos_z_2);
scene.add(cylindre_sph);
R_sph = (max_sph_2*max_sph_2 + rayon_2*rayon_2)/(2*max_sph_2);
var geometry = new THREE.SphereGeometry( R_sph, 40, 40 );
var material = new THREE.MeshBasicMaterial( {color: 0x00ffff} );
var sphere = new THREE.Mesh( geometry, material );
sphere.position.z = pos_z_2 + R_sph - max_sph_2 ;
scene.add( sphere );
var cylindre_bsp = new ThreeBSP(cylindre_sph);
var sphere_bsp = new ThreeBSP(sphere);
var inter = cylindre_bsp.subtract(sphere_bsp);
var result_1 = inter.toMesh(new THREE.MeshBasicMaterial({color : 0x0000ff}));
result_1.position.z = 15;
result_1.rotateX( Math.PI/2 );
scene.add(result_1);
Here is my two objets:
Cylinder and Sphere
....And the result :
After cylinder.subtract(sphere)...strange result
I don't understand why the substract betwwen the cylinder and the sphere give me these results.
thank you in advance :)
PS : I'm using three.js r74 and the latest version for threeCSG. Like it's impossible for the CSG to maintain the position of the Mesh with the r74 version ... but i can't change my version of three.js ^^''

Related

How to use Three.InstancedMesh in Aframe

I'm trying to implement instancing in Aframe using the ThreeJs InstancedMesh based on the example here: https://github.com/mrdoob/three.js/blob/master/examples/webgl_instancing_dynamic.html
Relevant section of code here:
init: function() {
const {count, radius, scale, colors, positions} = this.data;
this.start = true;
this.dummy = new THREE.Object3D();
this.count = count;
this.startObject = new THREE.Object3D();
this.endObject = new THREE.Object3D();
this.instanceColors = new Float32Array(count * 3);
this.instanceColorsBase = new Float32Array(this.instanceColors.length);
this.vertices = [];
this.rotations = [];
for ( var i = 0; i < this.data.count; i ++ ) {
var x = this.data.positions[i][0] * this.data.scale;
var y = this.data.positions[i][1] * this.data.scale;
var z = this.data.positions[i][2] * this.data.scale;
var xEnd = x + this.data.endPositions[i][0] * this.data.scale;
var yEnd = y + this.data.endPositions[i][1] * this.data.scale;
var zEnd = z + this.data.endPositions[i][2] * this.data.scale;
this.vertices.push( x, y, z );
const rotation = this.getDirection({'x':x,'y':y,'z':z},
{'x':xEnd,'y':yEnd,'z':zEnd});
this.rotations.push(rotation.x, rotation.y, rotation.z);
}
let mesh;
let geometry;
let material;
const loader = new THREE.GLTFLoader();
const el = this.el;
loader.load("/assets/arrow/arrow.gltf", function ( model ) {
geometry = model.scene.children[0].children[0].geometry;
geometry.computeVertexNormals();
geometry.scale( 0.03, 0.03, 0.03 );
material = new THREE.MeshNormalMaterial();
mesh = new THREE.InstancedMesh( geometry, material, count );
mesh.instanceMatrix.setUsage( THREE.DynamicDrawUsage );
el.object3D.add(mesh);
} );
this.el.setAttribute("id", "cells");
},
setMatrix: function (start) {
if (this.mesh) {
for ( let i = 0; i < this.count; i ++ ) {
var x = this.data.positions[i][0] * this.data.scale;
var y = this.data.positions[i][1] * this.data.scale;
var z = this.data.positions[i][2] * this.data.scale;
var xEnd = x + this.data.endPositions[i][0] * this.data.scale;
var yEnd = y + this.data.endPositions[i][1] * this.data.scale;
var zEnd = z + this.data.endPositions[i][2] * this.data.scale;
if (start) {
this.dummy.position.set(xEnd, yEnd, zEnd);
} else {
this.dummy.position.set(x, y, z);
}
this.dummy.rotation.x = this.rotations[i][0];
this.dummy.rotation.y = this.rotations[i][1];
this.dummy.rotation.z = this.rotations[i][2];
this.dummy.updateMatrix();
this.mesh.setMatrixAt( i, this.dummy.matrix );
}
this.mesh.instanceMatrix.needsUpdate = true;
}
}
tick: function() {
this.setMatrix(this.start);
this.start = !this.start;
},
No errors or relevant messages that I can see, but none of the instanced objects are rendering. I don't really have a good way to post an example unfortunately. Anyone know what I'm doing wrong? Thanks in advance!
Note: It seems that the objects are being rendered because the number of triangles being drawn increases drastically when I add this component. However, they are not visible anywhere and I can't find them in the aframe inspector either
It's a very case specific question with a quite extensive topic, so:
In general, using THREE.InstancedMeshes is simple, and you got it right:
// create an instanced mesh
let iMesh = new THREE.InstancedMesh(geometry, material, count)
element.object3D.add(iMesh)
// manipulate the instances
let mtx = new Matrix4()
// set the position, rotation, scale of the matrix
// ...
// update the instance
iMesh.setMatrixAt(index, mtx);
iMesh.instanceMatrix.needsUpdate = true;
Example of an instanced gltf model here
Your code is doing a lot, and it would be easier if it could be stripped to a bare minimum. Yet I think there is only one major issue - this.model isn't set anywhere, so the setMatrix function does nothing. Other than that you may need to disable frustum culling (mesh.frustumCulling = false), or set a bounding sphere - otherwise the objects may dissapear when the base object is out of sight.
Once it's set, your code seems to be working

three js - raycaster failing

I have a mesh defined which is at the origin.
var textureCanvas = new THREE.CanvasTexture( imageCanvas );
textureCanvas.repeat.set( 4, 4 );
textureCanvas.wrapS = THREE.RepeatWrapping;
textureCanvas.wrapT = THREE.RepeatWrapping;
var materialCanvas = new THREE.MeshBasicMaterial( { map: textureCanvas }
);
var geometry = new THREE.Geometry();
geometry.vertices.push(
new THREE.Vector3(-4,-4,0),
new THREE.Vector3(-4,4,0),
new THREE.Vector3(4,4,0),
new THREE.Vector3(4,-4,0),
);
geometry.faces.push(
new THREE.Face3(3, 1, 0),
new THREE.Face3(3, 2, 1)
);
var vertexMappings = [];
vertexMappings[0] = new THREE.Vector2(0,1);
vertexMappings[1] = new THREE.Vector2(0,0);
vertexMappings[2] = new THREE.Vector2(1,0);
vertexMappings[3] = new THREE.Vector2(1,1);
var vm = vertexMappings;
geometry.faceVertexUvs[ 0 ] = [];
geometry.faceVertexUvs[0][0] = [ vm[3], vm[1], vm[0] ];
geometry.faceVertexUvs[0][1] = [ vm[3], vm[2], vm[1] ];
meshCanvas = new THREE.Mesh( geometry, materialCanvas );
meshCanvas.rotation.x = - Math.PI / 3;
meshCanvas.rotation.z = - Math.PI / 2.5;
meshCanvas.scale.set( 80, 80, 80 );
scene.add( meshCanvas );
I also have a line which goes through the mesh. It was originally passing through the origin but I moved it a bit (See github issue below).
var linegeo = new THREE.Geometry();
linegeo.vertices.push(
new THREE.Vector3(55, 300, 0),
new THREE.Vector3(-10, -300, 32)
);
scene.add(linemesh);
I want to get the position where the line intersects the mesh, but the intersections result is always empty:
getInersectionPosition(linemesh, meshCanvas);
function getInersectionPosition(linemesh, meshCanvas) {
linemesh.updateMatrixWorld();
meshCanvas.updateMatrixWorld();
var p1 = linemesh.geometry.vertices[0].clone(),
p2 = linemesh.geometry.vertices[1].clone();
p1.applyMatrix4(linemesh.matrixWorld);
p2.applyMatrix4(linemesh.matrixWorld);
//console.log(`p1: ${JSON.stringify(p1)}, p2: ${JSON.stringify(p2)}`);
//console.log(`canvas position: ${JSON.stringify(meshCanvas.position)}`);
var raycaster = new THREE.Raycaster(p1, p2);
raycaster.linePrecision = 10;
//var intersections = raycaster.intersectObjects([meshCanvas]);
var intersections = raycaster.intersectObjects(scene.children, true);
if (intersections.length > 0)
console.log(`intersections: ${ intersections.length}`);
}
Full sample: https://jsfiddle.net/mribbons/103wwsda/
Is it possible that I have this issue?
https://github.com/mrdoob/three.js/issues/11449
The raycaster.intersectObjects() call seems to fail here, with very large values for distance (2.7 million or so, while sphere.radius is ~450).
https://github.com/mrdoob/three.js/blob/dev/build/three.js#L9761
intersectsSphere: function ( sphere ) {
var distance = this.distanceToPoint( sphere.center )
return distance <= sphere.radius;
},
My mistake, raycaster expects a point and a normal, this resolves the issue:
var normal = new THREE.Vector3();
normal.subVectors(p2, p1).normalize();
var raycaster = new THREE.Raycaster(p1, normal);
var intersections = raycaster.intersectObjects(scene.children, true);
if (intersections.length > 0)
console.log(`intersections: ${ intersections.length}`); // outputs "intersections: 2"

Substract 3D text to a Geometry in Three.js

I want to transform my 3D text to Geometry so I can use it with CSG (I used a Three.js CSG wrapper also) to substract it from another object like in this question.
My 3D text :
loader.load('optimer_regular.typeface.json', function (font){
var text = new THREE.TextGeometry('Tayst', {
font: font,
size: 4,
height: 3
});
var textMat = new THREE.MeshBasicMaterial({color: 0xffffff});
var textGeo = new THREE.Mesh(text, textMat);
textGeo.position.x = -7;
textGeo.position.z = 6;
scene.add(textGeo);
});
My substract thing I want to do for the 3D text (but here it's from circles):
var dots = new THREE.Geometry();
for(var i = 0; i < coordinates.length; i++){
var coords = coordinates[i];
sphereMesh.position.x = coords[0];
sphereMesh.position.y = coords[1];
sphereMesh.position.z = coords[2];
sphereMesh.updateMatrix();
dots.merge(sphereMesh.geometry, sphereMesh.matrix);
}
var sphereCsg = THREE.CSG.toCSG(dots);
var resultGeo = THREE.CSG.fromCSG(resultCsg.subtract(sphereCsg));
cube = new THREE.Mesh(resultGeo, material);
But the thing is, I think, I need to convert my text to a real Geometry so I can substract it?
Well i found out using the library I already got, but instead I didn't had to do the "merge" for example.
Here is the working code (in case someone needs it) :
var loader = new THREE.FontLoader();
loader.load('helvetiker_regular.typeface.json', function (font){
var text = new THREE.TextGeometry('Test', {
font: font,
size: 4,
height: 0.5
});
var textGeo = new THREE.Mesh(text);
textGeo.position.x = -5;
textGeo.position.z = 7.5;
var txtCsg = THREE.CSG.toCSG(textGeo);
var resultGeo = THREE.CSG.fromCSG(resultCsg.subtract(txtCsg));
cube = new THREE.Mesh(resultGeo, material);
scene.add(cube);
});

three.js fontloader cannot read generateshapes of undefined

I have the following code that generates some meshes. I want to add 3d text to the scene but when i do this i get the following error:
TypeError: Cannot read property 'generateShapes' of undefined
this is the code i have to generate the meshes and the 3d text:
var x = 0;
var y = 0;
var finalSize = 450;
var textureLoader = new THREE.TextureLoader();
var fontLoader = new THREE.FontLoader();
the var font is undefined why?
var font = fontLoader.load( 'css/arial_bold.json');
var fontColor = textMaterial = new THREE.MeshBasicMaterial({ color: 0x000000, overdraw: 0.5 });
for (var i = javascriptProjects.length - 1; i >= 0; i--) {
var object = {};
object.scale = javascriptProjects[i].baseImageData["0"] / finalSize;
var geometry = new THREE.PlaneGeometry(javascriptProjects[i].baseImageData["0"]/object.scale,javascriptProjects[i].baseImageData["1"]/object.scale, 10, 10);
object.texture = textureLoader.load( "data/"+ javascriptProjects[i].base_image );
object.material = new THREE.MeshBasicMaterial( { map: object.texture, overdraw: 0.5, transparent: true } );
object.material.opacity = 0.5;
object.mesh = new THREE.Mesh(geometry, object.material);
//the line below generates the error
object.FontGeo = new THREE.TextGeometry( javascriptProjects[i].project_name , {
font: font,
size: 50,
height: 2,
curveSegments: 12,
bevelThickness: 1,
bevelSize: 1,
bevelEnabled: false
});
object.textMesh = new THREE.Mesh( object.FontGeo, fontColor );
object.textMesh.position.x = object.location.x;
object.textMesh.position.y = object.location.y;
object.textMesh.position.z = object.location.z - 100;
scene.add(object.textMesh);
object.location = new THREE.Vector3(x, 120, y);
object.id = "" + javascriptProjects[i].project_id;
object.mesh.position.x = object.location.x;
object.mesh.position.y = object.location.y;
object.mesh.position.z = object.location.z;
scene.add(object.mesh);
meshes.push(object.mesh);
objects.push(object);
x += 600;
if(i % 3 == 0){
y += 600;
x = 0;
};
};
apparently font is undefined. why i don't see what i am doing wrong. Any suggestions would be great. If something is not clear let me know.
Thanks in advance
FontLoader.load() is an asynchronous function call. This is why there is an onLoad call back function.
The library is calling font.generateShapes() before the font is loaded.
Use a pattern like this one, instead:
var loader = new THREE.FontLoader();
loader.load( 'myFontFile.json', function ( font ) {
// insert your code here
} );
See, for example, this three.js example, and the three.js documentation .
three.js r.84

Javascript oop instances

I am developing with Javascript oop and Observer pattern.
In the first method model g is put inside mesh and mesh inside scene. We can find now our g model in scene.children. In the second method in intersected[i].object we can find our model g. The problem is that if I modify a property of intersected[i].object it is not reflected to the model g.
g = new GeometryModel();
tjsv = new ThreeJSView(document.getElementById('maincanvas'), g);
GeometryModel.prototype.populateScene = function(scene) {
var i;
for (i = 0; i < this.geometries.length; i++) {
var g = this.geometries[i];//<----- g is the model
var material = new THREE.MeshBasicMaterial( { color: g.color, transparent: g.transparent, opacity: g.opacity });
var mesh = new THREE.Mesh(g, material);
this.addLabels(g, mesh);
scene.add(mesh);
if (g.lineWidth > 0) {
var egh = new THREE.EdgesHelper( mesh, g.lineColor );
egh.material.linewidth = g.lineWidth;
scene.add( egh );
}
}
}
ThreeJSView.prototype.selectByRaycaster = function(x, y){
var i;
var intersected;
var mouse = {x: (x / this.width) * 2 - 1, y: 1 - (y / this.height) * 2};
var raycaster = new THREE.Raycaster();
raycaster.setFromCamera(mouse, this.camera);
intersected = raycaster.intersectObjects(this.scene.children);
console.log();
for ( var i = 0; i < intersected.length; i++ ) {
if (intersected[i].object instanceof THREE.Mesh){
intersected[i].object.selected = true;// I modify my model
changed = true;
console.log( intersected[i].object);
}
}
console.log(this.model);//my model but here there are no modify!
if (changed)
this.model.notifyListeners();
}
I think that g is referring to geometry while intersected[i].object is referring to an Object3D - different objects.
You probably need to write something like this:
intersected[i].object.geometry.selected = true;

Categories

Resources