How to make a mesh draggable only in x & y direction according to camera? - draggable

I want my child meshes to be draggable only in x & y direction with respect to the camera. I am using free camera of Babylon. Tried using the "Left Right Dragger" and "DragPlaneNormal" but could not achieve the desired result, the mesh is draggable in z direction.
const axis = new BABYLON.Vector3(0,1,0);
const leftRightDragger = new BABYLON.PointerDragBehavior({dragAxis: axis});
leftRightDragger.moveAttached = true;
clone_child[child].addBehavior(leftRightDragger);
or
var pointerDragBehavior = new BABYLON.PointerDragBehavior({dragPlaneNormal: new BABYLON.Vector3(1,1,0)});
clone_child[child].addBehavior(pointerDragBehavior);
pointerDragBehavior.useObjectOrientationForDragging = false;

use babylonjs life cycle method.
const mesh // the mesh now dragging
const zPos = mesh.position.z
scene.registerBeforeRender(() => {
mesh.position.z = zPos
})

Related

Move Threejs mesh to the center of another mesh

I have the following structure in my scene (imported from glb):
I try to move the mesh svgGroupsvg_test to the center of mesh named M_Top (red cross is the expected location).
Here is my code:
function engraveSVG(object, value) {
//object is the name of the target mesh
var svgMeshName = 'svgGroup' + value
loadSVGandFit(svgMeshName, object, value).then(res => {
var svgMesh = scene.getObjectByName(svgMeshName);
svgMesh.scale.set(0.1, 0.1, 1)
const axesHelper = new THREE.AxesHelper( 20 );
svgMesh.parent.add(axesHelper)
moveCenterMeshToOtherMeshCenter(svgMesh, scene.getObjectByName(object))
})
}
I tried the following functions:
function moveCenterMeshToOtherMeshCenter(centerMesh, otherMesh) {
// get the center positions of both meshes in the local world
const centerMeshPosition = new THREE.Vector3();
const otherMeshPosition = new THREE.Vector3();
centerMesh.getWorldPosition(centerMeshPosition);
otherMesh.getWorldPosition(otherMeshPosition);
// calculate the difference between the center positions of both meshes
const difference = otherMeshPosition.sub(centerMeshPosition);
// translate the center mesh by the difference
centerMesh.translateX(difference.x);
centerMesh.translateY(difference.y);
centerMesh.translateZ(difference.z);
}
function moveCenterToOther(centerMesh, otherMesh) {
const centerBox = new THREE.Box3().setFromObject(centerMesh);
const otherBox = new THREE.Box3().setFromObject(otherMesh);
const centerPosition = centerBox.getCenter(new THREE.Vector3());
const otherPosition = otherBox.getCenter(new THREE.Vector3());
const offset = new THREE.Vector3().subVectors(otherPosition, centerPosition);
centerMesh.position.add(offset);
}
Is there something wrong ? Get center return a value around 0.
I chose to add the svg mesh in the same group of my target mesh. But it changes nothing.
The axes is also in the local coordinate.
Any help would be very appreciate.
I found a solution. I add my SVG in the root of the scene. I am now able to get the correct bounding box size and center in the world coordinates.

THREE.js planeGeometry clipped inside the walls

I want to make a 3D building using Three.js. For example, I made 6 walls and a floor by checkerboard texture. I used clippingPlanes for wall1 and wall4:
floor1.material.clippingPlanes = [plane1,plane4];
I made my planes(plane1 and plane4) by my walls(wall1 and wall4). For example, my wall4 planeGeometry and plane4 code is here:
var wallGeometry4 = new THREE.PlaneGeometry(40, Floor_Height, 1, 1);
var wall4 = createMesh(wallGeometry4, "brick_diffuse.jpg", THREE.DoubleSide, 1024, 1024);
unit1.add(wall4);
wall4.position.x = -10;
wall4.position.y = 0;
wall4.position.z = -20;
wall4.rotation.y = 1.5 * Math.PI;
wall4.add(new THREE.EdgesHelper(wall4, 0x000000));
var plane4 = new THREE.Plane();
var normal4 = new THREE.Vector3();
var point4 = new THREE.Vector3();
normal4.set(0, 0, -1).applyQuaternion(wall4.quaternion);
point4.copy(wall4.position);
plane4.setFromNormalAndCoplanarPoint(normal4, point4);
But I see an empty area between wall5 and wall6, because plane4(that used for clipping the floor) isn't the same size of wall4. I think Plane4 is whole of the scene. How to change size of my plane to clip correctly? Or Is there any way to make floor bounded in walls?
One way to achieve this as suggested is to use ShapeGeometry.
When you are creating your walls you can save the x and z co-ordinate of their starting and ending points in an array to form a loop of points of Vector2. Then you can create a new custom shape from these points using shapeGeometry.
points = [{x:0,y:0},{x:0,y:10},{x:10,y:10},{x:10,y:0},{x:0,y:0}]
function getShapeFromPoints(points){
const shape = new THREE.Shape();
shape.moveTo(points[0].x, points[0].y);
for (let i = 1; i < points.length; i++) {
shape.lineTo(points[i].x, points[i].y);
}
return shape;
}
function createPlaneFromPoints(points) {
const planeMaterial = getPlaneMaterial();
const shape = getShapeFromPoints(points);
const geometry = new THREE.ShapeBufferGeometry(shape);
geometry.rotateX(degreeToRadians(-90));
const mesh = new THREE.Mesh(geometry, planeMaterial);
return mesh;
}
Hope that helps you!

ShapePath auto closing

I'm having an issue with ShapePath where the shape is automatically being closed when converting to geometry?
var path = new THREE.ShapePath();
path.moveTo(0, 0);
path.lineTo(0,50);
path.lineTo(50,50);
var shapes = path.toShapes(true);
for ( var j = 0; j < shapes.length; j ++ ) {
var shape = shapes[ j ];
console.log(shape);
var material = new THREE.MeshLambertMaterial( {
color: Math.random() * 0xffffff
});
var geometry = new THREE.ShapeBufferGeometry( shape );
var mesh = new THREE.Mesh( geometry, material );
scene.add(mesh);
}
JS Fiddle
What I'm expecting to have is a L style shape, instead i'm getting a triangle being rendered.
I really just want to extrude the path to a solid L shape.
Any ideas on the best approach?
You can draw your intended result if you create an enclosing contour of the L shape like shown in the following example: https://jsfiddle.net/zbeLk6jw/1/
var path = new THREE.ShapePath();
path.moveTo(0, 0);
path.lineTo(0,50);
path.lineTo(10,50);
path.lineTo(10,10);
path.lineTo(25,10);
path.lineTo(25,0);

how to move multiple objects after scene.add(object);

I am using Three.js to create an animation of 100 cubes.
For each cube I set its geometry and material and then add it to the scene with:
scene.add(cube);
I have been storing the positions of the cubes in three arrays: cube_x[99] cube_y[99] cube_z[99]
I do not know how to tell the renderer their positions as they are modified. The following works for one cube when I have only one cube in the scene:
cube.position.x=300;
cube.position.y=000;
I tried making cube into an array to hold the xyz positions of 100 cubes. But I can not seem to call scene.add(cube[i]);
How can I update the position of 100 cubes after scene.add(cube);
How do I let three.js know which cube I am trying to move?
Thanks - javascript beginner
You would basically store a reference to each cube in an array:
// reference array
var cubes = [];
// create cube mesh
var cube = new THREE.Mesh(new THREE.BoxGeometry(), someMaterial);
// push a reference to array
cubes.push(cube);
// add same cube to scene
scene.add(cube);
Now you can iterate over each cube and move it:
cubes.forEach(function(cube) {
cube.position.z -= 0.1; // move for example in Z direction
});
You would do this within your animation loop.
// Setup a simple demo scene
var scene = new THREE.Scene();
var cam = new THREE.PerspectiveCamera(40, innerWidth / innerHeight, 0.1, maxDist);
var renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setPixelRatio(devicePixelRatio);
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
// STore our cubes somewhere, dist for random generation and clipping
var cubes = [], maxDist = 500, r = Math.random, materials = [];
for(var i = 0; i < 7; i++) materials.push(new THREE.MeshBasicMaterial({color: (r()*0xffffff)&0xffffff|0x999999}));
// Add and store some cubes
for(i = 0; i < 3000; i++) {
var cube = new THREE.Mesh(new THREE.BoxBufferGeometry(), materials[i % materials.length]);
cubes.push(cube); // store to our array
cube.position.set(r()*maxDist*2-maxDist, r()*maxDist*2-maxDist, r()*maxDist*2-maxDist); // random positions
}
scene.add(...cubes); //es6, add to scene
cam.position.z = maxDist;
// Animate cubes
function loop(time) {
for(i = 0, cube; i < cubes.length; i++) { // for each cube do:
cube = cubes[i];
cube.position.z -= 1; // move on Z in this demo
if (cube.position.z < -maxDist) cube.position.z = maxDist; // reset position for demo
}
cam.rotation.z = Math.sin(time*0.0003); // add some camera action
cam.rotation.y = Math.sin(time*0.0005)*0.3; // add some camera action
renderer.render(scene, cam); // render everything
requestAnimationFrame(loop)
};
requestAnimationFrame(loop)
body {margin:0:overflow:hidden}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/88/three.min.js"></script>

THREE.JS UV Mapping on ExtrudeGeometry

I need to apply a texture on a ExtrudeGeometry object.
The shape is a circle and the extrude path is composed of 2 vectors :
One for the top.
One for the bottom.
I didn't choose cylinderGeometry because I need to place top/bottom sections of my geometry at precise positions and because the geometry created will not be always purely vertical (like a oblique cylinder for example).
Here is a picture of a section (one top vector, one bottom vector and a shape extruded between these 2 vectors).
and a picture of the texture I'm trying to apply.
All I want to do is to wrap this picture on the vertical sides of my object just one time.
Here is my code :
var biVectors = [ new THREE.Vector3( this.startVector.x, this.startVector.y, this.startVector.z ) , new THREE.Vector3( this.endVector.x, this.endVector.y, this.endVector.z ) ];
var wellSpline = new THREE.SplineCurve3(biVectors);
var extrudeSettings = {
steps : 1,
material: 0,
extrudeMaterial: 1,
extrudePath : wellSpline
};
var pts = [];
for (var i = 0; i <= this.segments; i++) {
var theta = (i / this.segments) * Math.PI * 2;
pts.push( new THREE.Vector3(Math.cos(theta) * this.diameter , Math.sin(theta) * this.diameter, 0) );
}
var shape = new THREE.Shape( pts );
var geometry = new THREE.ExtrudeGeometry( shape, extrudeSettings );
var texture = THREE.ImageUtils.loadTexture( 'textures/sampleTexture2.jpg' );
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
texture.flipY = false;
var material = new THREE.MeshBasicMaterial( { map: texture } );
var slice = new THREE.Mesh( geometry, material );
var faceNormals = new THREE.FaceNormalsHelper( slice );
console.log("face normals: ", faceNormals);
myCanvas.scene.add( faceNormals );
slice.parentObject = this;
myCanvas.scene.add( slice );
this.object3D = slice;
}
Now, as you can see, the mapping is not correct at all.
I've read a lot of information about this problem the last 3 days. But I'm running out of options as I'm new to THREE.JS.
I think I have to redefine the UV coordinates but I have no clue how to do this.
It seems that wrapping a texture on a cylinder like object is anything but easy in THREE.JS.
Can someone please help me on this issue ?

Resources