THREEJS: add hole to rendered Shape - three.js

I'm trying to inject hole to the shape that is already added to the scene, but some thing going wrong...
so in details: the shape
var well,
vertices = [],
wellShape,
wellMaterial = new THREE.MeshLambertMaterial({color: this.params.wellsColor});
vertices.push(new THREE.Vector2(0,3000));
vertices.push(new THREE.Vector2(4000,3000));
vertices.push(new THREE.Vector2(4000,0));
vertices.push(new THREE.Vector2(0,0));
wellShape = new THREE.Shape(vertices);
well = new THREE.Mesh( new THREE.ShapeGeometry(wellShape), wellMaterial);
scene.add(well);
well.geometry.dynamic = true;
var hole = [
new THREE.Vector3(300,300,0),
new THREE.Vector3(1000,300,0),
new THREE.Vector3(1000,1000,0),
new THREE.Vector3(300,1000,0)
];
well.geometry.vertices = well.geometry.vertices.concat(hole);
well.geometry.faces = [];
var triangles = THREE.Shape.Utils.triangulateShape ( well.geometry.vertices, hole );
for( var i = 0; i < triangles.length; i++ ){
well.geometry.faces.push( new THREE.Face3( triangles[i][0], triangles[i][1], triangles[i][2] ));
well.geometry.faceVertexUvs[ 0 ][i] = THREE.ExtrudeGeometry.WorldUVGenerator.generateTopUV(well.geometry, triangles[i][0], triangles[i][1], triangles[i][2]);
}
but as result i got something strange: in console output "Infinite Loop! Holes left:4, Probably Hole outside Shape!" and on desktop i got https://yadi.sk/i/WHRzH7c2jnaRm
could someone tell me what is wrong in my code?

after few days play around I found what is wrong:
1. THREE.Shape.Utils.triangulateShape expect vertices and holes to be parts of same shape.
2. hole should be not the array of points but array of path.
3. vertices should be concated after triangulation.
so correct result is:
....
var holePoints = [
new THREE.Vector3(300,300,0),
new THREE.Vector3(1000,300,0),
new THREE.Vector3(1000,1000,0),
new THREE.Vector3(300,1000,0)
],
hole = new THREE.Path();
hole.fromPoints(holePoints);
var shape = new THREE.Shape(well.geometry.vertices);
shape.holes = [hole];
var points = shape.extractPoints();
well.geometry.faces = [];
var triangles = THREE.Shape.Utils.triangulateShape ( points.vertices, points.holes );
....
Hope someone will find this info helpful.

Related

Points not showing up in Threejs

I am trying to use Points object in threejs. I am trying to display a point, but it is not showing up. Browser does not give any error. Let me know what needs to be done. Thanks
let pointGeometry:any = new BufferGeometry();
let positionAttribute = new BufferAttribute( new Float32Array([10,5,10]), 10 );
pointGeometry.setAttribute( 'position', positionAttribute );
pointGeometry.attributes.position.needsUpdate = true;
pointGeometry.setDrawRange(0,10);
let pointMaterial = new PointsMaterial( { color: 0x03F9E7, size: 10 } );
this.point = new Points( pointGeometry, pointMaterial );
this.point.name = 'point';
this.point.scale.copy( new Vector3(10,10,10) );
this.scene.add( this.point );
The itemSize of BufferAttribute should be 3.
let positionAttribute = new BufferAttribute( new Float32Array([10,5,10]), 3 );
pointGeometry.setAttribute( 'position', positionAttribute );
itemSize -- the number of values of the array that should be associated with a particular vertex. For instance, if this attribute is storing a 3-component vector (such as a position, normal, or color), then itemSize should be 3.
[Docs] BufferAttribute
Another reason may be that the camera looks to the wrong position. If you set a scale of 10 the final position of the point will be at [100,50,100].

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!

ThreeJS, Updating mesh.geometry at runtime

Considering a scene and 3 BoxBufferGeometries, I don't want to re-render the scene (drawObjects is called once, the 'first' time), but rather update it (updateScene is called on user action for example), the following code sucessfully updates the color and texture material attributes for all the objects of the scene, but only seems to affect one of the 3 objects when it comes to mesh.geometry transformation (one becomes as sphere, the 2 other remains squares), why? (live example)
drawObjects = (options) ->
map = (new (THREE.TextureLoader)).load(options.texture)
map.wrapS = map.wrapT = THREE.RepeatWrapping
map.anisotropy = 16
material = new (THREE.MeshToonMaterial)(
color: options.color
map: map
transparent: true
side: THREE.DoubleSide)
geometry = new THREE.BoxBufferGeometry( 20, 20, 20 )
i = 0
while i < 3
object = new (THREE.Mesh)(geometry, material, (color: Math.random() * 0xffffff))
object.position.x = Math.random() * 800 - 400
object.position.y = Math.random() * 800 - 400
object.position.z = Math.random() * 800 - 400
object.setGeometry = ->
geometry = new THREE.SphereGeometry(20)
object.geometry = geometry
return
object.setTexture = (texture) ->
object.material.map = new THREE.TextureLoader().load( texture )
return
object.setColor = (color) ->
object.material.color = new THREE.Color(color)
return
scene.add object
i++
updateScene = (options) ->
#scene.traverse (object) ->
if object.isMesh == true
object.setColor(options.color)
object.setTexture(options.texture)
object.setGeometry()
return
I tried few other approches aswell, the first was about updating vertices (like indicated here) but it doesn't work since BoxGeometry do no have vertices. Then I tied to add a few options such as, but without success.
# object.geometry.dynamic = true
# object.geometry.buffersNeedUpdate = true
# object.updateMatrix()
The second one was using object dispose basically making a clone of the original, like advised here dispose the original mesh and replace it with its clone such as but it didn't work in my case since I have functions attached to this object I'll need to re-use. Maybe I did it wrong:
object.geometry.dispose();
object.geometry = geometry.clone();
I bet there is a way to achieve this kind mesh.geometry transformation and apply it to every object while traversing the scene, but can't find how.
Kind regards
Ended up doing it with a function out of the object itself:
setGeometries = (geometry) ->
#scene.traverse (object) ->
if object
object.geometry = geometry
object.geometry.buffersNeedUpdate = true
return
return
return
Where geometry is something like new THREE.[Primitive]BuffferGeometry. Works for me.

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.

Three JS : Attaching an object to another

I'm trying to attach a cube to another cube at a specific point. I'm able to get collision and .add() is working fine. However, I want to add child cube exactly at point where collision occurs to the parent cube.
for (var vertexIndex = 0; vertexIndex < MovingCube.geometry.vertices.length; vertexIndex++) {
var localVertex = MovingCube.geometry.vertices[vertexIndex].clone();
var globalVertex = localVertex.applyMatrix4( MovingCube.matrix );
var directionVector = globalVertex.sub( MovingCube.position );
var ray = new THREE.Raycaster( originPoint, directionVector.clone().normalize() );
var collisionResults = ray.intersectObjects( collidableMeshList );
if ( collisionResults.length > 0 && collisionResults[0].distance < directionVector.length() )
console.log(" Hit and collision detected " + SELECTED);
MovingCubeBig.add(SELECTED);
// Is there way to get collision point and attach object at that point?
}
Intersections results should include a point attribute.
https://github.com/mrdoob/three.js/blob/master/src/core/Raycaster.js#L255
Maybe you can use this to attach the child at the right position ?
Also, this example may help http://threejs.org/examples/#webgl_geometry_terrain_raycast

Resources