Three.js + Tween.js tweening vertices of multiple objects - three.js

I am using three.js to create multiple BoxGeometries and tween.js to animate all vertices of the geometry.
At first all cubes should randomly distort untill i call stopTweens(). Then all vertices should go back to their default posiiton.
My problem is, that only the last cube gets reseted visually. The others are also tweened, but it seems like verticesNeedUpdate is not set to true and so nothing is happening on the screen.
Here is my Code:
var scene_03_TweenArr = [];
Function to create Cubes:
function createCubes(){
for (i = 0; i < 10; i++) {
var CubeGeometry = new THREE.BoxGeometry( 4, 4, 4, 5, 5, 5 );
var CubeMaterial = new THREE.MeshPhongMaterial( {color: 0xffffff} );
var cube = new THREE.Mesh( CubeGeometry, CubeMaterial );
scene_03.add( cube );
cube.position.x = -40+5*i;
tt_animate_vertices(cube);
}
}
Function to animate Cubes randomly:
function tt_animate_vertices(object) {
for( j = 0; j < object.geometry.vertices.length; j++ ){
var previousX = object.geometry.vertices[j].x;
var previousY = object.geometry.vertices[j].y;
var previousZ = object.geometry.vertices[j].z;
var tween = new TWEEN.Tween(object.geometry.vertices[j]);
tween.to({ x: (0.25*Math.random()+1)*previousX, y: (0.25*Math.random()+1)*previousY, z: (0.25*Math.random()+1)*previousZ }, 5000 + Math.random()*5000);
var tween2 = new TWEEN.Tween(object.geometry.vertices[j]);
tween2.to({ x: previousX, y: previousY, z: previousZ }, 5000 + Math.random()*5000);
tween.onUpdate(function(){
object.geometry.verticesNeedUpdate = true;
});
tween2.onUpdate(function(){
object.geometry.verticesNeedUpdate = true;
});
tween.chain(tween2);
tween2.chain(tween);
tween.start();
var verticeTween = [object, tween, tween2, j, previousX, previousY, previousZ ];
scene_03_TweenArr.push(verticeTween);
}
}
Function to stop tweens and reset cubevertices to default:
function stopTweens(){
for (let k = 0; k < scene_03_TweenArr.length; k++) {
scene_03_TweenArr[k][1].stop();
scene_03_TweenArr[k][2].stop();
var object = scene_03_TweenArr[k][0];
var index = scene_03_TweenArr[k][3];
var prevX = scene_03_TweenArr[k][4];
var prevY = scene_03_TweenArr[k][5];
var prevZ = scene_03_TweenArr[k][6];
var tween = new TWEEN.Tween(object.geometry.vertices[index]);
tween.to({x: prevX, y: prevY, z: prevZ }, 300)
tween.onUpdate(function(){
object.geometry.verticesNeedUpdate = true;
});
tween.start();
object.geometry.verticesNeedUpdate = true;
}
}
Here I managed to make it run in JSFiddle with the same behaviour:
http://jsfiddle.net/gfhyvou3/25/

From my experience, it's better to use let keyword inside loops, when you work with arrays.
function stopTweens(){
for (let k = 0; k < scene_03_TweenArr.length; k++) {
scene_03_TweenArr[k][1].stop();
scene_03_TweenArr[k][2].stop();
let object = scene_03_TweenArr[k][0];
let index = scene_03_TweenArr[k][3];
let prevX = scene_03_TweenArr[k][4];
let prevY = scene_03_TweenArr[k][5];
let prevZ = scene_03_TweenArr[k][6];
var tween = new TWEEN.Tween(object.geometry.vertices[index]);
tween.to({x: prevX, y: prevY, z: prevZ }, 300)
tween.onUpdate(function(){
object.geometry.verticesNeedUpdate = true;
});
tween.start();
object.geometry.verticesNeedUpdate = true;
}
}

Related

Three.js. Get localToWorld from BufferGeometry vertices

I get faces from geometry attributes
get the coordinates for each point of the face
draw lines for each edge of the face
And everything is fine if the object is not moved:
without moving
But if you move, then the lines remain in place:
after moving
If I try to get the world coordinates of points after moving, then I see the same picture:
after moving + localToWorld
If I update matrix world for cube, then I see this:
after moving + localToWorld + updateMatrixWorld
var scene = new THREE.Scene();
scene.background = new THREE.Color().setStyle('#e0e0e0');
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 100000);
camera.position.set(0, 500, 3000);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
///////////////////////CREATE CUBE////////////////////////////////
const cube = new THREE.Mesh(
new THREE.BoxBufferGeometry(1000, 1000, 1000),
new THREE.MeshBasicMaterial({
color: 'red',
transparent: true,
opacity: 0.4
})
);
scene.add(cube);
//MOVE
cube.position.set(100, 100, 100);
//UPDATE
cube.updateMatrixWorld(true);
//GET EDGE LINES(THREE.Line3) FOR EDGES OF FACES
const lines = getLinesFromFaces(cube);
//CONVERT FROM LOCAL TO WORLD
lines.map(l => {
//l.applyMatrix4(cube.matrixWorld);
//l.start.applyMatrix4(cube.matrixWorld);
//l.end.applyMatrix4(cube.matrixWorld);
cube.localToWorld(l.start);
cube.localToWorld(l.end);
});
//DRAW
drawLines(lines);
function drawLines(lines) {
for (let i = 0; i < lines.length; i += 1) {
addLine(lines[i].start, lines[i].end);
}
}
function addLine(p1, p2) {
const material = new THREE.LineBasicMaterial({
color: 0x0000ff
});
const points = [p1, p2];
const geometry = new THREE.BufferGeometry().setFromPoints(points);
const line = new THREE.Line(geometry, material);
scene.add(line);
}
function getLinesFromFaces(object) {
const facesWithPos = getFacesWithPos(object.geometry);
const lines = [];
for (let i = 0; i < facesWithPos.length; i += 1) {
const f = facesWithPos[i];
const lineAB = new THREE.Line3(f.a, f.b);
let isExist = false;
isExist = lines.some(l => {
return (l.start.equals(lineAB.start) && l.end.equals(lineAB.end)) ||
(l.start.equals(lineAB.end) && l.end.equals(lineAB.start));
});
if (!isExist) lines.push(lineAB);
const lineBC = new THREE.Line3(f.b, f.c);
isExist = false;
isExist = lines.some(l => {
return (l.start.equals(lineBC.start) && l.end.equals(lineBC.end)) ||
(l.start.equals(lineBC.end) && l.end.equals(lineBC.start));
});
if (!isExist) lines.push(lineBC);
const lineCA = new THREE.Line3(f.c, f.a);
isExist = false;
isExist = lines.some(l => {
return (l.start.equals(lineCA.start) && l.end.equals(lineCA.end)) ||
(l.start.equals(lineCA.end) && l.end.equals(lineCA.start));
});
if (!isExist) lines.push(lineCA);
}
return lines;
}
function getFacesWithPos(geometry) {
const faces = getFaces(geometry);
const facesWithPos = [];
const position = geometry.getAttribute('position');
for (let i = 0; i < faces.length; i += 1) {
const f = faces[i];
facesWithPos.push({
a: new THREE.Vector3(position.array[f.a * 3], position.array[f.a * 3 + 1], position.array[f.a * 3 + 2]),
b: new THREE.Vector3(position.array[f.b * 3], position.array[f.b * 3 + 1], position.array[f.b * 3 + 2]),
c: new THREE.Vector3(position.array[f.c * 3], position.array[f.c * 3 + 1], position.array[f.c * 3 + 2])
});
}
return facesWithPos;
}
function getFaces(geometry) {
const faces = [];
const index = geometry.getIndex();
for (let i = 0; i < index.count; i += 3) {
faces.push({
a: index.getX(i),
b: index.getX(i + 1),
c: index.getX(i + 2)
});
}
return faces;
}
render();
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.117.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.117.0/examples/js/controls/OrbitControls.js"></script>
How to get world coordinates? what am I doing wrong?
Some of the line points were common objects. If you apply localToWorld to them, then the method was applied to them several times and the result was not correct. Below is the solution
var scene = new THREE.Scene();
scene.background = new THREE.Color().setStyle('#e0e0e0');
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 100000);
camera.position.set(0, 500, 3000);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
///////////////////////CREATE CUBE////////////////////////////////
const cube = new THREE.Mesh(
new THREE.BoxBufferGeometry(1000, 1000, 1000),
new THREE.MeshBasicMaterial({
color: 'red',
transparent: true,
opacity: 0.4
})
);
scene.add(cube);
//MOVE
cube.position.set(100, 100, 100);
//UPDATE
cube.updateMatrixWorld(true);
//GET EDGE LINES(THREE.Line3) FOR EDGES OF FACES
const lines = getLinesFromFaces(cube);
//CONVERT FROM LOCAL TO WORLD
lines.map(l => {
//l.applyMatrix4(cube.matrixWorld);
//l.start.applyMatrix4(cube.matrixWorld);
//l.end.applyMatrix4(cube.matrixWorld);
cube.localToWorld(l.start);
cube.localToWorld(l.end);
});
//DRAW
drawLines(lines);
function drawLines(lines) {
for (let i = 0; i < lines.length; i += 1) {
addLine(lines[i].start, lines[i].end);
}
}
function addLine(p1, p2) {
const material = new THREE.LineBasicMaterial({
color: 0x0000ff
});
const points = [p1, p2];
const geometry = new THREE.BufferGeometry().setFromPoints(points);
const line = new THREE.Line(geometry, material);
scene.add(line);
}
function getLinesFromFaces(object) {
const facesWithPos = getFacesWithPos(object.geometry);
const lines = [];
for (let i = 0; i < facesWithPos.length; i += 1) {
const f = facesWithPos[i];
const lineAB = new THREE.Line3(f.a, f.b);
let isExist = false;
isExist = lines.some(l => {
return (l.start.equals(lineAB.start) && l.end.equals(lineAB.end)) ||
(l.start.equals(lineAB.end) && l.end.equals(lineAB.start));
});
if (!isExist) lines.push(lineAB.clone());
const lineBC = new THREE.Line3(f.b, f.c);
isExist = false;
isExist = lines.some(l => {
return (l.start.equals(lineBC.start) && l.end.equals(lineBC.end)) ||
(l.start.equals(lineBC.end) && l.end.equals(lineBC.start));
});
if (!isExist) lines.push(lineBC.clone());
const lineCA = new THREE.Line3(f.c, f.a);
isExist = false;
isExist = lines.some(l => {
return (l.start.equals(lineCA.start) && l.end.equals(lineCA.end)) ||
(l.start.equals(lineCA.end) && l.end.equals(lineCA.start));
});
if (!isExist) lines.push(lineCA.clone());
}
return lines;
}
function getFacesWithPos(geometry) {
const faces = getFaces(geometry);
const facesWithPos = [];
const position = geometry.getAttribute('position');
for (let i = 0; i < faces.length; i += 1) {
const f = faces[i];
facesWithPos.push({
a: new THREE.Vector3(position.array[f.a * 3], position.array[f.a * 3 + 1], position.array[f.a * 3 + 2]),
b: new THREE.Vector3(position.array[f.b * 3], position.array[f.b * 3 + 1], position.array[f.b * 3 + 2]),
c: new THREE.Vector3(position.array[f.c * 3], position.array[f.c * 3 + 1], position.array[f.c * 3 + 2])
});
}
return facesWithPos;
}
function getFaces(geometry) {
const faces = [];
const index = geometry.getIndex();
for (let i = 0; i < index.count; i += 3) {
faces.push({
a: index.getX(i),
b: index.getX(i + 1),
c: index.getX(i + 2)
});
}
return faces;
}
render();
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.117.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.117.0/examples/js/controls/OrbitControls.js"></script>

Three.js - previous lines become invisible after cloning

I'm attempting to duplicate an array of lines, but when I do, the previous ones somehow become dark or invisible. The previous lines can still be accessed with all their properties and I tested them also having the correct location. I shift both sets of lines, and the previous ones are not visible. Does anyone know what the problem could be?
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
var lines = []
currentIndex = 0;
addLines()
duplicateLines(0)
var lineColorGradientList = [
0x100FFF,
0x0F76FF,
0x0FE0FF,
0x0FFFB3,
0x0FFF4A,
0x3DFF0F,
0xA7FF0F,
0xFFED0F
]
function addLines(){
var linesArray = []
for(i=0; i<8; ++i){
var geometry = new THREE.Geometry();
geometry.vertices.push(
new THREE.Vector3(0, 0, 2),
new THREE.Vector3(3, 0, 0));
var color = lineColorGradientList[i]
var material = new THREE.LineBasicMaterial({
color: color
});
var line = new THREE.Line( geometry, material );
line.geometry.verticesNeedUpdate = true;
line.material.needsUpdate = true
linesArray.push(line)
}
linesArray.forEach(function(line) {
line.frustumCulled = false;
scene.add(line);
});
lines.push(linesArray)
}
function duplicateLines(selectedIndex){
currentIndex += 1
var linesArray = []
for(i=0; i<8; ++i){
var line = lines[selectedIndex][i].clone()
line.geometry.verticesNeedUpdate = true;
line.material.needsUpdate = true
linesArray.push(line)
}
lines.push(linesArray)
lines[currentIndex].forEach(function(line) {
line.frustumCulled = false;
scene.add(line);
});
}
You also need to clone the geometry in order for it to work.
My corrected duplicateLinesFunction:
function duplicateLines(selectedIndex){
currentIndex += 1
var linesArray = []
for(i=0; i<8; ++i){
var line = lines[selectedIndex][i].clone()
line.geometry = lines[selectedIndex][i].geometry.clone()
line.geometry.verticesNeedUpdate = true;
line.material = lines[selectedIndex][i].material.clone()
line.material.needsUpdate = true
linesArray.push(line)
}
lines.push(linesArray)
lines[currentIndex].forEach(function(line) {
line.frustumCulled = false;
scene.add(line);
});
}

Draw a 2D line with width in three.js

I'm looking to draw a continuous line with a given thickness showing only the edges using three.js. I have achieved it. I'm trying to add thickness to the line but it is not getting reflected in the scene due to some angle in three.js. Can anyone help me out with the issue.
Here's the fiddle https://jsfiddle.net/16vhjm0y/1/
var renderer, scene, camera;
var line;
var count = 0;
var mouse = new THREE.Vector3();
var mesh3D;
var maxPoint = 6;
var height = window.innerHeight * .99;
var plane = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0); // facing us for mouse intersection
var raycaster = new THREE.Raycaster();
var point3ds = [];
var usePerspectiveCamera = false; // toggles back and forth
var perspOrbit;
var perspCam;
var orthoOrbit;
var orthoCam;
var labelRenderer, labelAjay;
var testBoolean = false;
var mouseDownBoolean = false;
var distanceData, showDistanceData;
var ajay;
var arrAjay = [];
var arrAjayFinal = [];
var mouseUpBoolean = false;
init();
animate();
function init() {
// renderer
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, height);
document.body.appendChild(renderer.domElement);
// scene
scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);
// camera perspective
perspCam = new THREE.PerspectiveCamera(45, window.innerWidth / height, 1, 10000);
perspCam.position.set(0, 0, 200);
// camera ortho
var width = window.innerWidth;
//var height = window.innerHeight;
orthoCam = new THREE.OrthographicCamera(-width / 2, width / 2, height / 2, -height / 2, 0, 1200);
// assign cam
camera = perspCam;
someMaterial = new THREE.MeshBasicMaterial({ color: 0xA9A9A9, side: THREE.DoubleSide, transparent: true, opacity: 0.3 });
// grid
var grid = new THREE.GridHelper(1024, 56);
grid.rotateX(Math.PI / 2);
// scene.add(grid);
// geometry
var geometry = new THREE.BufferGeometry();
var MAX_POINTS = 500;
positions = new Float32Array(MAX_POINTS * 3);
geometry.addAttribute('position', new THREE.BufferAttribute(positions, 3));
// material
var material = new THREE.LineBasicMaterial({
color: 0xff0000,
linewidth: 10
});
// line
line = new THREE.Line(geometry, material);
// line.position.z = 20;
scene.add(line);
// var geometry = new THREE.BoxBufferGeometry( 10, 2, 20 );
// var edgesPavement = new THREE.EdgesGeometry( geomPavement );
// var lineGeometry = new THREE.LineSegmentsGeometry().setPositions( edgesPavement.attributes.position.array );
// var lineMaterial = new THREE.LineMaterial( { color: 0xff0000, linewidth: 10 } );
// lineMaterial.resolution.set( window.innerWidth, window.innerHeight ); // important, for now...
// var line = new THREE.LineSegments2( lineGeometry, lineMaterial );
// scene.add( line );
document.addEventListener("mousemove", onMouseMove, false);
document.addEventListener('mousedown', onMouseDown, false);
document.addEventListener('mouseup', onMouseUp, false);
createUI();
labelRenderer = new THREE.CSS2DRenderer();
ajay = document.createElement('div');
ajay.className = 'ajay';
ajay.style.color = "black";
ajayInsert = document.createElement('div');
ajayInsert.className = 'ajay';
ajayInsert.style.color = "black";
// ajay.style.color = "black";
// console.log(ajay)
labelAjay = new THREE.CSS2DObject(ajay);
labelAjayFinal = new THREE.CSS2DObject(ajayInsert);
labelRenderer.setSize(window.innerWidth, window.innerHeight);
labelRenderer.domElement.style.position = 'absolute';
labelRenderer.domElement.style.top = '0';
labelRenderer.domElement.style.pointerEvents = 'none';
ajay.style.display = "none";
ajayInsert.style.display = "none";
}
// update line
function updateLine() {
positions[count * 3 - 3] = mouse.x;
positions[count * 3 - 2] = mouse.y;
positions[count * 3 - 1] = mouse.z;
line.geometry.attributes.position.needsUpdate = true;
}
// mouse move handler
function onMouseMove(event) {
var rect = renderer.domElement.getBoundingClientRect();
mouse.x = (event.clientX - rect.left) / (rect.right - rect.left) * 2 - 1;
mouse.y = - ((event.clientY - rect.top) / (rect.bottom - rect.top)) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
mouse = raycaster.ray.intersectPlane(plane, mouse);
if (count !== 0 && count < maxPoint) {
updateLine();
}
testBoolean = true;
if (testBoolean == true) {
// scene.remove(labelAjay);
var geometry = line.geometry;
geometry.computeBoundingBox();
center = geometry.boundingBox.getCenter();
// line.localToWorld(center);
// console.log(center);
if (mouseDownBoolean == true) {
labelAjay.position.set(mouse.x, mouse.y, mouse.z);
// console.log(line.position)
scene.add(labelAjay);
document.body.appendChild(labelRenderer.domElement);
// console.log(positions);
distanceData = point3ds[0].distanceTo(new THREE.Vector3(mouse.x, mouse.y, mouse.z));
showDistanceData = Math.round(distanceData * 1000);
// console.log(point3ds[0]);
// console.log(point3ds[1]);
// console.log(distanceData);
// console.log(showDistanceData)
ajay.textContent = showDistanceData + ' mm';
// console.log(labelRenderer)
}
// console.log(labelRenderer.domElement)
// document.getElementsByClassName("ajay").remove();
// document.getElementsByClassName("ajay").outerHTML = "";
}
}
// add point
function addPoint(event) {
if (count < maxPoint) {
console.log("point nr " + count + ": " + mouse.x + " " + mouse.y + " " + mouse.z);
positions[count * 3 + 0] = mouse.x;
positions[count * 3 + 1] = mouse.y;
positions[count * 3 + 2] = mouse.z
count++;
line.geometry.setDrawRange(0, count);
updateLine();
point3ds.push(new THREE.Vector3(mouse.x, mouse.y, mouse.z));
} else {
console.log('max points reached: ' + maxPoint);
}
}
function getPointInBetweenByLen(pointA, pointB, length) {
var dir = pointB.clone().sub(pointA).normalize().multiplyScalar(length);
return pointA.clone().add(dir);
}
// mouse down handler
function onMouseDown(evt) {
mouseDownBoolean = true;
// force add an extra point on first click so buffer line can display
// buffer geometry requires two points to display, so first click should add two points
if (count === 0) {
addPoint();
}
if (count < maxPoint) {
addPoint();
}
}
function onMouseUp(event){
mouseUpBoolean = true;
if(mouseUpBoolean == true){
// showDistanceData = Math.round(distanceData * 1000);
arrAjay.push(showDistanceData);
console.log(arrAjay);
arrAjayFinal = arrAjay.splice(-1)[0];
var geometry = line.geometry;
geometry.computeBoundingBox();
center = geometry.boundingBox.getCenter();
if (mouseDownBoolean == true) {
labelAjayFinal.position.set(center.x, center.y, center.z);
scene.add(labelAjayFinal);
document.body.appendChild(labelRenderer.domElement);
// distanceData = point3ds[0].distanceTo(new THREE.Vector3(mouse.x, mouse.y, mouse.z));
// showDistanceData = Math.round(distanceData * 1000);
console.log('arrAjayFinal', arrAjayFinal);
ajayInsert.textContent = arrAjayFinal;
}
}
}
// render
function render() {
renderer.render(scene, camera);
labelRenderer.render(scene, camera);
}
// animate
function animate() {
requestAnimationFrame(animate);
render();
}
// loop through all the segments and create their 3D
function create3D() {
if (!mesh3D && point3ds && point3ds.length) {
console.log('creating 3D');
mesh3D = new THREE.Mesh(); // metpy mesh but is the root mesh for all 3D
scene.add(mesh3D);
// prepare create segments from point3ds - every two points create a segement
var index = 1;
var segmentHeight = 56;
point3ds.forEach(point3d => {
if (index < point3ds.length) {
var seg = new Segment(point3d, point3ds[index], someMaterial, segmentHeight);
mesh3D.add(seg.mesh3D);
index++;
}
});
}
}
function createUI() {
// create3D
var btn = document.createElement('button');
document.body.appendChild(btn);
btn.innerHTML = 'Create3D';
btn.addEventListener('mousedown', () => {
create3D();
// add orbiting controls to both cameras
var controls;
if (!perspOrbit) {
perspOrbit = new THREE.OrbitControls(perspCam, renderer.domElement);
perspOrbit.screenSpacePanning = true;
// raotation is enabled once create3D is pressed
setToFullOrbit(perspOrbit);
perspOrbit.enabled = true; // set to true by default
}
// add orbit to orthocam
if (!orthoOrbit) {
orthoOrbit = new THREE.OrbitControls(orthoCam, renderer.domElement);
orthoOrbit.screenSpacePanning = true;
orthoOrbit.enabled = false; // set to false by default
//orthoOrbit.enableDamping = true;
//orthoOrbit.dampingFactor = .15;
}
});
}
function switchCam() {
usePerspectiveCamera = !usePerspectiveCamera;
if (usePerspectiveCamera) {
if (perspCam) {
camera = perspCam;
perspOrbit.enabled = true;
orthoOrbit.enabled = false;
} else {
throw new Error('Switch to perspective cam failed, perspective cam is null');
}
} else {
if (orthoCam) {
camera = orthoCam;
orthoOrbit.enabled = true;
perspOrbit.enabled = false;
} else {
throw new Error('Switch to ortho cam failed, orthoCam is null');
}
}
}
function rotateCam90() {
if (camera instanceof THREE.OrthographicCamera) {
orthoOrbit.update();
camera.applyMatrix(new THREE.Matrix4().makeRotationZ(Math.PI / 2));
}
}
function reset() {
scene.remove(mesh3D);
mesh3D = null;
for (var i = 0; i < 3 * 8; i++) {
positions[i] = 0;
}
count = 0;
line.geometry.setDrawRange(0, count);
updateLine();
point3ds = [];
}
function setToFullOrbit(orbitControl) {
// how far you can orbit vertically
orbitControl.minPolarAngle = 0;
orbitControl.maxPolarAngle = Math.PI;
// How far you can dolly in and out ( PerspectiveCamera only )
orbitControl.minDistance = 0;
orbitControl.maxDistance = Infinity;
orbitControl.enableZoom = true; // Set to false to disable zooming
orbitControl.zoomSpeed = 1.0;
orbitControl.enableRotate = true;
// allow keyboard arrows
orbitControl.enableKeys = true;
// Set to false to disable panning (ie vertical and horizontal translations)
orbitControl.enablePan = true;
}
// each segment knows how to create its 3D
class Segment {
constructor(start, end, material, height) {
this.start = start;
this.end = end;
this.height = height; // height of the segment's 3D
this.material = material;
this.mesh3D = null;
this.create3D();
}
create3D() {
if (this.start && this.end) {
//create the shape geometry
var distStartToEnd = this.start.distanceTo(this.end);
var vec2s = [
new THREE.Vector2(),
new THREE.Vector2(0, this.height),
new THREE.Vector2(distStartToEnd, this.height),
new THREE.Vector2(distStartToEnd, 0)
];
console.log('vec2s', vec2s);
var shape = new THREE.Shape(vec2s);
var geo = new THREE.BoxGeometry(5, 5, 5);
// console.log('shape', shape);
var geo = new THREE.ShapeGeometry(shape);
geo.applyMatrix(new THREE.Matrix4().makeRotationX(THREE.Math.degToRad(90)));
this.mesh3D = new THREE.Mesh(geo, this.material);
this.alignRotation();
this.alignPosition();
// the mesh3D should be added to the scene outside of this class
}
}
alignRotation() {
var p1 = this.start.clone();
var p2 = this.end.clone();
var direction = new THREE.Vector3();
direction.subVectors(p2, p1);
direction.normalize();
this.mesh3D.quaternion.setFromUnitVectors(new THREE.Vector3(1, 0, 0), direction);
}
alignPosition() {
if (this.mesh3D) {
this.mesh3D.position.copy(this.start);
} else {
throw new Error('mesh3D null');
}
}
}
The linewidth parameter relies on native WebGL support for drawing line thickness, but its performance is very spotty across browsers & operating systems. I think Windows doesn't support it, but MacOS does, so it shouldn't be relied upon. See this discussion on the Three.js Github for several bug reports.
As a workaround, they've created LineGeometry, which sort of re-builds a line with geometry to allow for thickness. See this example for how to use it. It even allows for dashed lines. After importing the module, you can implement it with:
const geometry = new LineGeometry();
geometry.setPositions( positions );
geometry.setColors( colors );
matLine = new LineMaterial( {
color: 0xffffff,
linewidth: 5, // in pixels
vertexColors: true,
dashed: false
} );
line = new Line2( geometry, matLine );
line.computeLineDistances();
scene.add( line );

Can not get uniform speed when camera move along the curve

I want to make camera move along the curve,it works, but when pass the turning corner,camera speed changed,looks like slowly.
curve = new THREE.CatmullRomCurve3(vectors);
curve.type = 'catmullrom';
curve.tension = 0.2;
this.MKY.Camera.current = this.roamCamera;
cameraWrap.add(this.roamCamera);
this.MKY.scene.add(cameraWrap);
this.MKY.update.push(roam);
function roam() {
if(!isAutoRoam){return}
if(progress>1 || progress==1){
progress = 0;
return
}
progress += 0.0005;
var position = curve.getPointAt(progress);
position.y += 1.5;
var tangent = curve.getTangentAt(progress);
cameraWrap.position.copy(position);
cameraWrap.lookAt(position.sub(tangent));
};
getPointAt returns a vector for point at a relative position in curve according to arc length. I think if progress not change ,i will get the average speed,but it is not. I do not understend.
There is the approach, using .getUtoTmapping() method of THREE.Curve() (in the example, it's THREE.CatmullRomCurve3).
The documentation says:
.getUtoTmapping ( u, distance )
Given u in the range ( 0 .. 1 ), returns t also in the range ( 0 .. 1 ). u and t can then be used to give you points which are equidistant from the ends of the curve, using .getPoint.
So, when you provide the second parameter in this method, then, if I got it correctly from the source code, it ignores the first parameter, thus you can find the point on your curve by the distance on it.
In the given picture:
small yellow points - points, taken with .getPoints() method;
big maroon points - points, whose distance between each other along the curve is 1 unit;
The code for the maroon points:
var unitPoints = [];
for (let i = 0; i < spline.getLength(); i++){
let p = spline.getUtoTmapping(0, i);
let p1 = spline.getPoint(p);
unitPoints.push(p1);
}
var unitPointsGeometry = new THREE.Geometry();
unitPointsGeometry.vertices = unitPoints;
var units = new THREE.Points(unitPointsGeometry, new THREE.PointsMaterial({size: .125, color: "maroon"}));
scene.add(units);
Look at the source code of the code snippet and pay attention to the getProgress() function.
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, .1, 1000);
camera.position.set(0, 1.5, 3);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x181818);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
scene.add(new THREE.GridHelper(4, 8));
var spline = new THREE.CatmullRomCurve3(
[
new THREE.Vector3(-2, 0, 0),
new THREE.Vector3(-1.9, .1, .1),
new THREE.Vector3(1, 1, 1),
new THREE.Vector3(0, -1, -2),
new THREE.Vector3(2, 0, 1)
]
);
spline.closed = true;
var splinePoints = spline.getPoints(200);
var lineGeom = new THREE.Geometry();
lineGeom.vertices = splinePoints;
var line = new THREE.Line(lineGeom, new THREE.LineBasicMaterial({
color: "orange"
}));
scene.add(line);
var sPoints = new THREE.Points(lineGeom, new THREE.PointsMaterial({
size: .0312,
color: "yellow"
}));
scene.add(sPoints);
var unitPoints = [];
for (let i = 0; i < spline.getLength(); i++) {
let p = spline.getUtoTmapping(0, i);
let p1 = spline.getPoint(p);
unitPoints.push(p1);
}
var unitPointsGeometry = new THREE.Geometry();
unitPointsGeometry.vertices = unitPoints;
var units = new THREE.Points(unitPointsGeometry, new THREE.PointsMaterial({
size: .125,
color: "maroon"
}));
scene.add(units);
var marker = new THREE.Mesh(new THREE.SphereGeometry(0.125, 4, 2), new THREE.MeshBasicMaterial({
color: "red",
wireframe: true
}));
marker.geometry.translate(0, 0, 0.0625);
marker.geometry.vertices[2].z = 0.25;
marker.geometry.vertices[4].z = 0;
scene.add(marker);
var markerLineGeometry = new THREE.Geometry();
markerLineGeometry.vertices.push(new THREE.Vector3(), new THREE.Vector3());
var line = new THREE.Line(markerLineGeometry, new THREE.LineBasicMaterial({
color: "white"
}));
scene.add(line);
var clock = new THREE.Clock();
var progress = 0;
var totalLength = spline.getLength();
var speed = .66; // unit a second
var ratio = speed / totalLength;
var shift = 0;
var basePoint = 0;
var lookAtPoint = 0;
var oldPosition = spline.getPoint(0);
var speedVector = new THREE.Vector3();
function setProgress(delta) {
if (progress > totalLength) progress = 0;
shift = progress + speed * 2;
shift = shift > totalLength ? shift - totalLength : shift;
basePoint = spline.getUtoTmapping(0, progress);
lookAtPoint = spline.getUtoTmapping(0, shift);
line.geometry.vertices[0].copy(spline.getPoint(basePoint));
line.geometry.vertices[1].copy(spline.getPoint(lookAtPoint));
line.geometry.verticesNeedUpdate = true;
marker.position.copy(line.geometry.vertices[0]);
marker.lookAt(line.geometry.vertices[1]);
progress += speed * delta;
}
render();
function render() {
requestAnimationFrame(render);
setProgress(clock.getDelta());
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

How to make transparent hole to object which has image texture in THREE.js?

I made a house with walls, ceiling and floor.
Now I am trying to make holes in walls for windows/doors.
But there is an issue in textures of the walls.
This is function to build wall:
function build_wall(start, end, materialFront, id){
var dx = end.x - start.x;
var dy = end.y - start.y;
var wall_length = Math.sqrt(dx*dx + dy*dy);
var centroid_x = start.x + dx/2;
var centroid_y = (start.y + dy/2) * -1;
var ry = Math.atan2(dy, dx);
var materialBack = new THREE.MeshLambertMaterial( { color: 0xd9d9d9, shading: THREE.FlatShading, side: THREE.BackSide} );
var materialTop = new THREE.MeshBasicMaterial({color: 0xb3b3b3, side: THREE.DoubleSide});
var materials = [materialFront, materialBack, materialTop];
var material = new THREE.MeshFaceMaterial(materials);
var rectShape = new THREE.Shape();
rectShape.moveTo( 0, 0 );
rectShape.lineTo( 0, wall_height );
rectShape.lineTo( wall_length, wall_height );
rectShape.lineTo( wall_length, 0 );
rectShape.lineTo( 0, 0 );
var windowHole = new THREE.Path();
windowHole.moveTo(20, 180);
windowHole.lineTo(20, 160);
windowHole.lineTo(40, 160);
windowHole.lineTo(40, 180);
rectShape.holes.push(windowHole);
var extrudeSettings = { amount: 5, bevelEnabled: true, bevelSegments: 0, steps: 1, bevelSize: 0, bevelThickness: 1 };
var wall = new THREE.ExtrudeGeometry( rectShape, extrudeSettings );
for ( var face in wall.faces ) {
if (wall.faces[ face ].normal.z > 0.9) wall.faces[ face ].materialIndex = 0;
else if (wall.faces[ face ].normal.z < -0.9) wall.faces[ face ].materialIndex = 1;
else wall.faces[ face ].materialIndex = 2;
}
var wall_mesh = new THREE.Mesh(wall, material);
wall_mesh.position.set( start.x, 0, -start.y );
wall_mesh.rotation.set(0, ry, 0);
wall_mesh.name = id;
wall_mesh.data = 'wall';
scene.add(wall_mesh);
}
and I am calling this function in init():
//------Add Walls
coordArray.push(coordArray[0]); //push the first corner to close loop
for(var i = 0; i < coordArray.length-1; i++){ //iterate over the coordinate array, pushing vertices to the geometry
var start_wall = coordArray[i];
var end_wall = coordArray[(i+1)];
if(!Rooms[r].wall_show || Rooms[r].wall_show[i] == 1){
var wallTexture = new THREE.TextureLoader().load( "images/room_" + r + "_wall_" + i + ".jpg" );
var wall_material = new THREE.MeshBasicMaterial({
map: wallTexture,
side: THREE.FrontSide,
overdraw: 0.5
});
build_wall( start_wall, end_wall, wall_material, scene_id);
}
//find tour boundary, find center target
if(start_wall.x > maxX) maxX = start_wall.x;
if(start_wall.y > maxY) maxY = start_wall.y;
if(start_wall.x < minX) minX = start_wall.x;
if(start_wall.y < minY) minY = start_wall.y;
}
The result is as following:
The screenshot of the result
Sorry, It was cuz I didn't adjust the UVs to the [ 0, 1 ] range.
var uvs = wall.faceVertexUvs[0];
for (var i = 0; i < uvs.length; i++) {
uv = uvs[i];
for (var j = 0; j < uv.length; j++) {
u = uv[j];
u.x = u.x / wall_length;
u.y = u.y/ wall_height;
}
}
var wall_mesh = new THREE.Mesh(wall, material);
wall_mesh.position.set( start.x, 0, -start.y );
wall_mesh.rotation.set(0, ry, 0);
wall_mesh.name = id;
wall_mesh.data = 'wall';
scene.add(wall_mesh);

Resources