Related
I'm trying customizing mouse cursor javascript on my own html but it doesn't work well.
This is javascript code and I get it from "https://codepen.io/dlch/pen/eWXgyo"
var camera, renderer, scene, particleSystem, baseParticle, mouse;
window.onload = function () {
mouse = [window.innerWidth / 2, window.innerHeight / 2];
renderer = new THREE.WebGLRenderer({ antialias: true });
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(20, window.innerWidth / window.innerHeight, 0.1, 1000);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
camera.position.z = 50;
scene.background = new THREE.Color(0x333344);
canvas = document.querySelector('#b canvas');
baseParticle = new THREE.PlaneGeometry(1, 1, 1);
baseParticle.applyMatrix(new THREE.Matrix4().makeRotationFromEuler(new THREE.Euler(0, 0, Math.PI / 4)));
for (var i = 0; i < baseParticle.vertices.length; i++) {
if (Math.round(baseParticle.vertices[i].y) != 0) {
baseParticle.vertices[i].x = 0;
baseParticle.vertices[i].z = 0;
}
}
baseParticle.mergeVertices();
baseParticle.verticesNeedUpdate = true;
baseParticle = new THREE.Mesh(baseParticle, new THREE.MeshBasicMaterial({ color: 0xffffff, emissive: 0x555555 }));
particleSystem = new ParticleSystem(99);
render();
};
window.onresize = function () {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
};
window.onmousemove = function (e) {
mouse = [e.clientX, e.clientY];
};
function randomFloat(a, b) {
var r = Math.random() * (b - a) + a;
return r;
}
function partToHex(part) {
var h = part.toString(16);
return h.length == 1 ? "0" + h : h;
}
console.log(partToHex(255));
var color;
function FireParticle() {
this.direction;
this.scaleSpeed;
this.curAge;
this.parent;
this.obj;
this.colorRamp = [[255, 255, 0], [255, 136, 34], [255, 17, 68], [153, 136, 136]];
this.update = function () {
if (Math.abs(this.parent.pos.x - this.obj.position.x) > 10 || Math.abs(-this.parent.pos.y - this.obj.position.y) > 10) {
this.obj.scale.x *= .8;
this.obj.scale.y *= .8;
this.obj.scale.z *= .8;
}
var point = this.curAge / 40;
var pointRem = point % 1;
if (Math.round(point) >= this.colorRamp.length - 1) {
color = this.colorRamp[this.colorRamp.length - 1];
} else {
color = [Math.floor(this.colorRamp[Math.floor(point)][0] * (1 - pointRem) + this.colorRamp[Math.floor(point) + 1][0] * pointRem), Math.floor(this.colorRamp[Math.floor(point)][1] * (1 - pointRem) + this.colorRamp[Math.floor(point) + 1][1] * pointRem), Math.floor(this.colorRamp[Math.floor(point)][2] * (1 - pointRem) + this.colorRamp[Math.floor(point) + 1][2] * pointRem)];
}
color = partToHex(color[0]) + partToHex(color[1]) + partToHex(color[2]);
color = parseInt(color, 16);
this.obj.material.color.setHex(color);
this.curAge++;
if (this.obj.scale.x < .01) {
this.init();
}
this.obj.position.x += this.direction.x;
this.obj.position.y += this.direction.y;
this.obj.position.z += this.direction.z;
this.obj.scale.x *= this.scaleSpeed;
this.obj.scale.y *= this.scaleSpeed;
this.obj.scale.z *= this.scaleSpeed;
};
this.init = function () {
this.direction = new THREE.Vector3(randomFloat(-.01, .01), randomFloat(.01, .1), randomFloat(-.01, .01));
this.scaleSpeed = randomFloat(.8, .99);
this.curAge = 0;
if (this.obj != undefined) {
scene.remove(this.obj);
}
this.obj = baseParticle.clone();
this.obj.position.set(this.parent.obj.position.x + randomFloat(-.2, .2), this.parent.obj.position.y, this.parent.obj.position.z + randomFloat(-.2, .2));
this.obj.scale.set(1, 2, 1);
this.obj.material = this.obj.material.clone();
// var size = randomFloat(.5, 1);
// this.obj.scale.set(size, 2*size, size);
for (var i = 0; i < randomFloat(0, 100); i++) {
this.update();
}
scene.add(this.obj);
};
}
function ParticleSystem(size) {
this.particles = [];
this.obj = new THREE.Group();
this.p = new THREE.Vector3();
this.d;
this.dis;
this.pos = new THREE.Vector3(0, 0, 0);
this.init = function () {
for (var i = 0; i < size; i++) {
this.particles.push(new FireParticle());
this.particles[i].parent = this;
this.particles[i].init();
}
scene.add(this.obj);
};
this.init();
this.update = function () {
this.p.set(mouse[0] / window.innerWidth * 2 - 1, mouse[1] / window.innerHeight * 2 - 1, .5);
this.p.unproject(camera);
this.d = this.p.sub(camera.position).normalize();
this.dis = -camera.position.z / this.d.z;
this.pos = camera.position.clone().add(this.d.multiplyScalar(this.dis));
this.obj.position.x = this.pos.x;
this.obj.position.y = -this.pos.y;
for (var i = 0; i < this.particles.length; i++) {
this.particles[i].update();
}
this.obj.rotation.y += .03;
};
}
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
particleSystem.update();
}
It just come out bottom of my html surrounded code author's background which is grey, not inside my html.
This particle fire mouse cursor doesn't show when it get out of code author's background.
Could you help me how to solve this problem?
I want to remove author's background and only want to use mouse cursor in my own html.
Please help me.
I have a fully working model of a car mesh moving right, left, up and down using Three.JS
The input data are acceleration on the X and Z direction, we apply double integration on the acceleration to compute the displacement. So I have been able to animate the car in all directions and making the background move to keep the car in the center of the canvas. However, I only need to background to move in the right and left direction, and not for the up and down
That's my code:
<html>
<head>
<style>
canvas {
position: fixed;
top: 0;
left: 0;
}
</style>
</head>
<body>
<script src="./libs/three.min.js"></script>
<script src="./libs/OrbitControls.js"></script>
<script src="./libs/KeyboardState.js"></script>
<script src="./libs/MTLLoader.js"></script>
<script src="./libs/OBJMTLLoader.js"></script>
<script src="./data/accXaccZCOMBINEDMOTIONS.json"></script>
</body>
<script>
var data = JSON.parse(JSON.stringify(data));
var toycar;
var valid = 1;
const dispArrayX = Array.from({ length: 1 }).fill(0);
const dispArrayZ = Array.from({ length: 1 }).fill(0);
let sensorValue = 0;
var initialVelocity = 0;
var angle = 0;
var currentIndex = 0;
var initialDisplacement = 0;
var scene, renderer, camera;
var width = window.innerWidth;
var height = window.innerHeight;
var time = data[currentIndex].time
var pos = new THREE.Vector3(0, 0, 0);
init();
animate();
function init() {
var width = window.innerWidth;
var height = window.innerHeight;
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setClearColor(0x626d73, 1);
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(10, width / height, 1, 10000);
// camera.position.y = -150;
// camera.position.y = 200;
// camera.position.x = 100;
camera.lookAt(new THREE.Vector3(0, 0, 0));
var loader = new THREE.OBJMTLLoader();
loader.load('https://jyunming-chen.github.io/tutsplus/models/toycar.obj', 'https://jyunming-chen.github.io/tutsplus/models/toycar.mtl',
function (object) {
toycar = object;
toycar.rotateZ(10.8); //toycar.rotateZ(-10.99);
scene.add(toycar);
});
var gridXZ = new THREE.GridHelper(100000, 10);
gridXZ.setColors(new THREE.Color(0xff0000), new THREE.Color(0xffffff));
scene.add(gridXZ);
var pointLight = new THREE.PointLight(0xffffff);
pointLight.position.set(350, 10, 5);
scene.add(pointLight);
var ambientLight = new THREE.AmbientLight(0x111111);
scene.add(ambientLight);
}
function animate(dt) {
let time = data[currentIndex].time
dt *= 10 ** -9
time += dt
while (data[currentIndex].time < time) {
currentIndex++
if (currentIndex >= data.length) return
}
const { accX,accZ } = data[currentIndex]
var dir = new THREE.Vector3(0, 0, -1);
dir.applyAxisAngle(new THREE.Vector3(0, 0, 0), 10);
pos.add(dir);
if (toycar != undefined) {
toycar.scale.set(0.1, 0.1, 0.1);
if (currentIndex > 0) {
// compute the displacement by double integrating the acceleration for accZ (e.i. Right and left movement)
const deltaTime = ((data[currentIndex].time) / (24 * 60 * 60)) - 1; // convert time from second per day to second
const velocityInitialZ = ((data[currentIndex-3].accZ + data[currentIndex-2].accZ)/2)*deltaTime; // compute initialVelocity two step backward
const velocityCurrentZ = velocityInitialZ + ((data[currentIndex-1].accZ + data[currentIndex].accZ)/2)*deltaTime; // compute currentVelocity one step backward
const previousDispZ = dispArrayZ[0] + (deltaTime * ((velocityCurrentZ + velocityInitialZ)/2));
dispArrayZ[0] = previousDispZ;
const dispCurrentZ = previousDispZ + dispArrayZ[0];
// compute the displacement by double integrating the acceleration for accX (e.i. Up and down movement)
const velocityInitialX = ((data[currentIndex-3].accX + data[currentIndex-2].accX)/2)*deltaTime; // compute initialVelocity two step backward
const velocityCurrentX = velocityInitialX + ((data[currentIndex-1].accX + data[currentIndex].accX)/2)*deltaTime; // compute currentVelocity one step backward
const previousDispX = dispArrayX[0] + (deltaTime * ((velocityCurrentX + velocityInitialX)/2));
dispArrayX[0] = previousDispX;
const dispCurrentX = previousDispX + dispArrayX[0];
const dispCurrentXscaled = dispCurrentX/3500;
// Move the car up and down
if (dispCurrentXscaled*0.0001 < 0){
toycar.position.x = dispCurrentXscaled*0.00001;
}
else if (dispCurrentXscaled*0.0001 > 8){
toycar.position.x = dispCurrentXscaled*0.0000001;
}
else{
toycar.position.x = dispCurrentXscaled*0.0001;
}
toycar.position.y = 0;
// Move the car right and left
toycar.position.z = -(dispCurrentZ/4000)*0.0005;
// print out displacementX and displacementZ
console.log("DispX: " + (dispCurrentX*0.0000001).toFixed(5) + " DispZ: " + ((dispCurrentZ/4000)*0.0005).toFixed(5));
}
toycar.rotation.x = (angle + Math.PI);;
var relativeCameraOffset = new THREE.Vector3(-1600, 400, 0);
var cameraOffset = relativeCameraOffset.applyMatrix4(toycar.matrixWorld);
camera.position.x = cameraOffset.x*0.5;
camera.position.y = cameraOffset.y;
camera.position.z = cameraOffset.z;
camera.lookAt(toycar.position);
}
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
</script>
</html>
I am using from a JSON file as input.
That's how the movement of the car looks like:
You can see that the background is following the car motion. For Up and Down I only need the car to move (not the background) and the right and left are fine because is moving so it stays within the canvas. Can you please help with that?
I'm making a 3D 4x4x4 tic tac toe with three js, and to check win combo condition, I created a boolean array. Since there are 16*4=64 blocks, I made a boolean array of size 64 and set it to false by default. And then whenever the user clicks one of the blocks it changes the clicked object to true dynamically.
To check the horizontal win condition, i used this,
var camera, scene, renderer, mesh, material, controls;
var targetList = [];
var targetListBool = new Array(64).fill(false);
console.log(targetListBool);
// var projector, mouse = { x: 0, y: 0 };
var projecter;
var mouse = new THREE.Vector2(),
INTERSECTED;
init();
animate();
addCubes();
render();
function addCubes() {
var xDistance = 30;
var zDistance = 15;
var geometry = new THREE.BoxBufferGeometry(10, 10, 10);
var material = new THREE.MeshBasicMaterial({
color: 0x6C70A8
});
//initial offset so does not start in middle.
var xOffset = -80;
//1
for (let i = 0; i < 4; i++) {
for (let j = 0; j < 4; j++) {
var mesh = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({
color: 0xadc9f4
}));
mesh.position.x = (xDistance * (i)) + xOffset;
mesh.position.z = (zDistance * (j));
scene.add(mesh);
targetList.push(mesh);
}
//2
for (let j = 0; j < 4; j++) {
var mesh2 = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({
color: 0xadc9f4
}));
mesh2.position.x = (xDistance * (i)) + xOffset;
mesh2.position.z = (zDistance * (j));
mesh2.position.y = 15;
scene.add(mesh2);
targetList.push(mesh2);
}
//3
for (let j = 0; j < 4; j++) {
var mesh3 = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({
color: 0xadc9f4
}));
mesh3.position.x = (xDistance * (i)) + xOffset;
mesh3.position.z = (zDistance * (j));
mesh3.position.y = 30;
scene.add(mesh3);
targetList.push(mesh3);
}
//4
for (let j = 0; j < 4; j++) {
var mesh4 = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({
color: 0xadc9f4
}));
mesh4.position.x = (xDistance * (i)) + xOffset;
mesh4.position.z = (zDistance * (j));
mesh4.position.y = 45;
scene.add(mesh4);
targetList.push(mesh4);
}
}
for (var i = 0; i < targetList.length; i++) {
targetList[i].name = i;
}
}
function init() {
// Renderer.
renderer = new THREE.WebGLRenderer({
antialias: true
});
// renderer = new THREE.WebGLRenderer();
//renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
// Add renderer to page
document.body.appendChild(renderer.domElement);
// Create camera.
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.z = 150;
// Add controls
controls = new THREE.TrackballControls(camera);
controls.addEventListener('change', render);
controls.target.set(0, 0, -50);
// Create scene.
scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);
// Create directional light and add to scene.
var pointLight = new THREE.PointLight(0xFFFFFF, 1, 100000);
pointLight.position.set(1, 1, 1).normalize();
scene.add(pointLight);
var directionalLight = new THREE.DirectionalLight(0xffffff);
directionalLight.position.set(1, 1, 1).normalize();
scene.add(directionalLight);
// Add listener for window resize.
window.addEventListener('resize', onWindowResize, false);
}
// initialize object to perform world/screen calculations
projector = new THREE.Projector();
// when the mouse moves, call the given function
document.addEventListener('mousedown', onDocumentMouseDown, false);
function onDocumentMouseDown(event) {
// the following line would stop any other event handler from firing
// (such as the mouse's TrackballControls)
event.preventDefault();
console.log("Click.");
// update the mouse variable
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// find intersections
// create a Ray with origin at the mouse position
// and direction into the scene (camera direction)
var vector = new THREE.Vector3(mouse.x, mouse.y, 1);
projector.unprojectVector(vector, camera);
var ray = new THREE.Raycaster();
ray.setFromCamera(mouse, camera);
// create an array containing all objects in the scene with which the ray intersects
var intersects = ray.intersectObjects(targetList);
// if there is one (or more) intersections
if (intersects.length > 0 && INTERSECTED != intersects[0].object) {
INTERSECTED = intersects[0].object;
INTERSECTED.material.emissive.setHex(0xff0000);
console.log(INTERSECTED.name);
// console.log("Hit # " + toString( intersects[0].point ) );
// change the color of the closest face.
// intersects[ 0 ].face.color.setHex(0xffa500);
// intersects[ 0 ].object.geometry.colorsNeedUpdate = true;
for (var i = 0; i < targetList.length; i++) {
if (INTERSECTED.name == i) {
targetListBool[i] = true;
}
}
console.log(targetListBool);
}
}
// $(intersec).click(function(){
// alert('you clicked number 1 block');
// });
function toString(v) {
return "[ " + v.x + ", " + v.y + ", " + v.z + " ]";
}
function animate() {
requestAnimationFrame(animate);
render();
controls.update();
}
function render() {
renderer.render(scene, camera);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
controls.handleResize();
}
for (let i = 0; i <targetListBool.length ; i+=4) {
if(targetListBool[i]
&&targetListBool[i+1]
&&targetListBool[i+2]
&&targetListBool[i+3]){
alert('win');
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>Tic tac toe</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
font-family: Monospace;
background-color: #f0f0f0;
margin: 0px;
overflow: hidden;
}
.info {
position: absolute;
background-color: black;
opacity: 0.8;
color: white;
text-align: center;
top: 0px;
width: 100%;
}
.info a {
color: #00ffff;
}
button {
display: hidden;
}
</style>
</head>
<body>
<div id="container">
<div>
<!-- <button id="restart">Restart</button> -->
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/threejs/r84/three.min.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/master/examples/js/controls/TrackballControls.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/master/examples/js/utils/BufferGeometryUtils.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/master/examples/js/libs/dat.gui.min.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/master/examples/js/renderers/Projector.js"></script>
</body>
</html>
I'm trying to check just horizontal win combination for a starter now.
for (let i = 0; i <targetListBool.length ; i+=4) {
if(targetListBool[i]
&&targetListBool[i+1]
&&targetListBool[i+2]
&&targetListBool[i+3]){
alert('win');
}
}
But it doesn't know that some values have changed by click event earier.
Just to clarify, it's supposed to alert 'win' if 4 consecutive horizontal blocks are clicked in each plane. but I guess something's wrong with the if statement in the for loop at the end of the snippet.
It's my first time using three js and i'm not really familiar with javaScript either. I would appreciate any help. Thanks.
Your system wins if it has 4 in a row on either the x,y or z axis. Your function only check the booleans in one direction. So best is to track the data in a 3 dimensional way. Here is an example of it. I manually set 4 on a row on the z axis and then do the check.
The check could and should be improved though. It's pretty inefficient, but kept it easy for the example and because I don't know your exact intentions. Should diagonals be checked too for example?
//Fill a variable with x,y,z coordinates with a boolean value that is false.
var locations = {};
for (var x = 1; x <= 4; x++) {
locations[x] = {};
for (var y = 1; y <= 4; y++) {
locations[x][y] = {};
for (var z = 1; z <= 4; z++) {
locations[x][y][z] = false;
}
}
}
//Set 4 values on the X axis to true for testing
locations[1][2][3] = true;
locations[2][2][3] = true;
locations[3][2][3] = true;
locations[4][2][3] = true;
//Set 4 values on the Z axis to true for testing
locations[1][2][1] = true;
locations[1][2][2] = true;
locations[1][2][3] = true;
locations[1][2][4] = true;
//Test if there are 4 on a row - note this can be done more efficient with a bit more thought and does not work for diagonals
var winX = false;
var winY = false;
var winZ = false;
for (var x = 1; x <= 4; x++) {
for (var y = 1; y <= 4; y++) {
for (var z = 1; z <= 4; z++) {
if(locations[x][y][z]) {
//check X for current position
if(locations[1][y][z] && locations[2][y][z] && locations[3][y][z] && locations[4][y][z]) {
winX = true;
}
//check Y for current position
if(locations[x][1][z] && locations[x][2][z] && locations[x][3][z] && locations[x][4][z]) {
winY = true;
}
//check Z for current position
if(locations[x][y][1] && locations[x][y][2] && locations[x][y][3] && locations[x][y][4]) {
winZ = true;
}
}
}
}
}
//Log the results, should return true for X and Z and false for Y
console.log("Win X: " + winX);
console.log("Win Y: " + winY);
console.log("Win Z: " + winZ);
I just started exploring three.js and have been trying to adapt a project I found.
I would like to know if it would be possible to have the globe object rotate around it's y-axis with minor additions to the code or whether it has to be rewritten from the ground up.
var canvas = document.querySelector('canvas');
var width = canvas.offsetWidth,
height = canvas.offsetHeight;
var colors = [
new THREE.Color(0xac1122),
new THREE.Color(0x96789f),
new THREE.Color(0x535353)];
var renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio > 1 ? 2 : 1);
renderer.setSize(width, height);
renderer.setClearColor(0xffffff);
var scene = new THREE.Scene();
var raycaster = new THREE.Raycaster();
raycaster.params.Points.threshold = 6;
var camera = new THREE.PerspectiveCamera(50, width / height, 0.1, 2000);
camera.position.set(0, 0, 350);
var galaxy = new THREE.Group();
scene.add(galaxy);
// Create dots
var loader = new THREE.TextureLoader();
loader.crossOrigin = "";
var dotTexture = loader.load("img/dotTexture.png");
var dotsAmount = 3000;
var dotsGeometry = new THREE.Geometry();
var positions = new Float32Array(dotsAmount * 3);
var sizes = new Float32Array(dotsAmount);
var colorsAttribute = new Float32Array(dotsAmount * 3);
for (var i = 0; i < dotsAmount; i++) {
var vector = new THREE.Vector3();
vector.color = Math.floor(Math.random() * colors.length);
vector.theta = Math.random() * Math.PI * 2;
vector.phi =
(1 - Math.sqrt(Math.random())) *
Math.PI /
2 *
(Math.random() > 0.5 ? 1 : -1);
vector.x = Math.cos(vector.theta) * Math.cos(vector.phi);
vector.y = Math.sin(vector.phi);
vector.z = Math.sin(vector.theta) * Math.cos(vector.phi);
vector.multiplyScalar(120 + (Math.random() - 0.5) * 5);
vector.scaleX = 5;
if (Math.random() > 0.5) {
moveDot(vector, i);
}
dotsGeometry.vertices.push(vector);
vector.toArray(positions, i * 3);
colors[vector.color].toArray(colorsAttribute, i*3);
sizes[i] = 5;
}
function moveDot(vector, index) {
var tempVector = vector.clone();
tempVector.multiplyScalar((Math.random() - 0.5) * 0.2 + 1);
TweenMax.to(vector, Math.random() * 3 + 3, {
x: tempVector.x,
y: tempVector.y,
z: tempVector.z,
yoyo: true,
repeat: -1,
delay: -Math.random() * 3,
ease: Power0.easeNone,
onUpdate: function () {
attributePositions.array[index*3] = vector.x;
attributePositions.array[index*3+1] = vector.y;
attributePositions.array[index*3+2] = vector.z;
}
});
}
var bufferWrapGeom = new THREE.BufferGeometry();
var attributePositions = new THREE.BufferAttribute(positions, 3);
bufferWrapGeom.addAttribute('position', attributePositions);
var attributeSizes = new THREE.BufferAttribute(sizes, 1);
bufferWrapGeom.addAttribute('size', attributeSizes);
var attributeColors = new THREE.BufferAttribute(colorsAttribute, 3);
bufferWrapGeom.addAttribute('color', attributeColors);
var shaderMaterial = new THREE.ShaderMaterial({
uniforms: {
texture: {
value: dotTexture
}
},
vertexShader: document.getElementById("wrapVertexShader").textContent,
fragmentShader: document.getElementById("wrapFragmentShader").textContent,
transparent:true
});
var wrap = new THREE.Points(bufferWrapGeom, shaderMaterial);
scene.add(wrap);
// Create white segments
var segmentsGeom = new THREE.Geometry();
var segmentsMat = new THREE.LineBasicMaterial({
color: 0xffffff,
transparent: true,
opacity: 0.3,
vertexColors: THREE.VertexColors
});
for (i = dotsGeometry.vertices.length - 1; i >= 0; i--) {
vector = dotsGeometry.vertices[i];
for (var j = dotsGeometry.vertices.length - 1; j >= 0; j--) {
if (i !== j && vector.distanceTo(dotsGeometry.vertices[j]) < 12) {
segmentsGeom.vertices.push(vector);
segmentsGeom.vertices.push(dotsGeometry.vertices[j]);
segmentsGeom.colors.push(colors[vector.color]);
segmentsGeom.colors.push(colors[vector.color]);
}
}
}
var segments = new THREE.LineSegments(segmentsGeom, segmentsMat);
galaxy.add(segments);
var hovered = [];
var prevHovered = [];
function render(a) {
var i;
dotsGeometry.verticesNeedUpdate = true;
segmentsGeom.verticesNeedUpdate = true;
raycaster.setFromCamera( mouse, camera );
var intersections = raycaster.intersectObjects([wrap]);
hovered = [];
if (intersections.length) {
for(i = 0; i < intersections.length; i++) {
var index = intersections[i].index;
hovered.push(index);
if (prevHovered.indexOf(index) === -1) {
onDotHover(index);
}
}
}
for(i = 0; i < prevHovered.length; i++){
if(hovered.indexOf(prevHovered[i]) === -1){
mouseOut(prevHovered[i]);
}
}
prevHovered = hovered.slice(0);
attributeSizes.needsUpdate = true;
attributePositions.needsUpdate = true;
renderer.render(scene, camera);
}
function onDotHover(index) {
dotsGeometry.vertices[index].tl = new TimelineMax();
dotsGeometry.vertices[index].tl.to(dotsGeometry.vertices[index], 1, {
scaleX: 10,
ease: Elastic.easeOut.config(2, 0.2),
onUpdate: function() {
attributeSizes.array[index] = dotsGeometry.vertices[index].scaleX;
}
});
}
function mouseOut(index) {
dotsGeometry.vertices[index].tl.to(dotsGeometry.vertices[index], 0.4, {
scaleX: 5,
ease: Power2.easeOut,
onUpdate: function() {
attributeSizes.array[index] = dotsGeometry.vertices[index].scaleX;
}
});
}
function onResize() {
canvas.style.width = '';
canvas.style.height = '';
width = canvas.offsetWidth;
height = canvas.offsetHeight;
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height);
}
var mouse = new THREE.Vector2(-100,-100);
function onMouseMove(e) {
var canvasBounding = canvas.getBoundingClientRect();
mouse.x = ((e.clientX - canvasBounding.left) / width) * 2 - 1;
mouse.y = -((e.clientY - canvasBounding.top) / height) * 2 + 1;
}
TweenMax.ticker.addEventListener("tick", render);
window.addEventListener("mousemove", onMouseMove);
var resizeTm;
window.addEventListener("resize", function(){
resizeTm = clearTimeout(resizeTm);
resizeTm = setTimeout(onResize, 200);
});
Codepen here - https://codepen.io/quickwaste/pen/PaGPdw
Thanks.
(A stretch goal would be to have the camera move in response to mouse movement)
Simply add galaxy.rotateY(0.005 * Math.PI); to render(), right before renderer.render(scene, camera) call, like this:
// pulled from the CodePen
function render(a) {
// ... omitted for brevity
prevHovered = hovered.slice(0);
attributeSizes.needsUpdate = true;
attributePositions.needsUpdate = true;
galaxy.rotateY(0.005 * Math.PI);
renderer.render(scene, camera);
}
I used a multiplier of 0.005 to give the globe a nice, lazy spin.
The 'galaxy' object is a THREE.Group, a wrapper of sorts for collections of THREE.Object3D objects. The Object3D has all sorts of nifty functions to help rotate, translate, and transform 3D objects. The rotateY() will spin the model around its local y-axis.
Can we draw "lines" with Cube to show "Dimensions" at run time?
Here is how I have created the cube and getting dimensions from user and changing the cube at run time: http://jsfiddle.net/9Lvk61j3/
But now I want to show the Dimension, so the user knows what the length, width, and height is, which they will be changing.
This is what I am trying to make as end result:
Here is my code:
HTML:
<script src="http://www.html5canvastutorials.com/libraries/three.min.js"></script>
<div id="container"></div>
<div class="inputRow clear" id="dimensionsNotRound" data-role="tooltip">
<label class="grid-8">Dimensions (pixels):</label>
<br/>
<br/>
<div> <span>Length</span>
<input class="numeric-textbox" id="inp-length" type="text" value="100">
<br/>
<br/>
</div>
<div> <span>Width</span>
<input class="numeric-textbox" id="inp-width" type="text" value="50">
<br/>
<br/>
</div>
<div> <span>Height</span>
<input class="numeric-textbox" id="inp-height" type="text" value="40">
<br/>
<br/>
</div>
<button id="btn">Click me to change the Dimensions</button>
JS
var shape = null;
//Script for 3D Box
// revolutions per second
var angularSpeed = 0.2;
var lastTime = 0;
var cube = 0;
// this function is executed on each animation frame
function animate() {
// update
var time = (new Date()).getTime();
var timeDiff = time - lastTime;
var angleChange = angularSpeed * timeDiff * 2 * Math.PI / 1000;
//cube.rotation.y += angleChange; //Starts Rotating Object
lastTime = time;
// render
renderer.render(scene, camera);
// request new frame
requestAnimationFrame(function () {
animate();
});
}
// renderer
var container = document.getElementById("container");
var renderer = new THREE.WebGLRenderer();
renderer.setSize(container.offsetWidth, container.offsetHeight - 4);
container.appendChild(renderer.domElement);
// camera
var camera = new THREE.PerspectiveCamera(60, container.offsetWidth / container.offsetHeight, 1, 1000);
camera.position.z = 800;
// scene
var scene = new THREE.Scene();
scene.remove();
// cube
cube = new THREE.Mesh(new THREE.CubeGeometry(1, 1, 1), new THREE.MeshLambertMaterial({
color: '#cccccc'
}));
cube.overdraw = true;
cube.rotation.x = Math.PI * 0.1;
cube.rotation.y = Math.PI * 0.3;
scene.add(cube);
// add subtle ambient lighting
var ambientLight = new THREE.AmbientLight(0x319ec5);
scene.add(ambientLight);
// directional lighting
var directionalLight = new THREE.DirectionalLight(0x666666);
directionalLight.position.set(1, 1, 1).normalize();
scene.add(directionalLight);
shape = cube;
// start animation
animate();
var $ = function(id) { return document.getElementById(id); };
$('btn').onclick = function() {
console.log("Button Clicked");
var width = parseInt(document.getElementById('inp-width').value * 3.779528),
height = parseInt(document.getElementById('inp-height').value * 3.779528),
length = parseInt(document.getElementById('inp-length').value * 3.779528);
console.log("length " + length + " height " + height + " width " + width);
shape.scale.x = length;
shape.scale.y = height;
shape.scale.z = width;
};
Here is the Fiddle for the same! http://jsfiddle.net/9Lvk61j3/
Let me know if you need any other information.
Please suggest.
There's a bit of a problem with drawing dimensions:
You may have many of them, and not all of them may be perfectly visible:
some may be hidden,
some may appear too small, if the camera is far away from the object,
some may overlay other dimensions (or even object elements),
some may be seen from inconvenient angle.
The text should retain perfectly same size, no matter how you navigate camera,
Most of these points are addressed in my solution: https://jsfiddle.net/mmalex/j35p1fw8/
var geometry = new THREE.BoxGeometry(8.15, 0.5, 12.25);
var material = new THREE.MeshPhongMaterial({
color: 0x09f9f9,
transparent: true,
opacity: 0.75
});
var cube = new THREE.Mesh(geometry, material);
cube.geometry.computeBoundingBox ();
root.add(cube);
var bbox = cube.geometry.boundingBox;
var dim = new LinearDimension(document.body, renderer, camera);
// define start and end point of dimension
var from = new THREE.Vector3(bbox.min.x, bbox.min.y, bbox.min.z);
var to = new THREE.Vector3(bbox.max.x, bbox.min.y, bbox.max.z);
// in which direction to "extrude" dimension away from object
var direction = new THREE.Vector3(0, 0, 1);
// request LinearDimension to create threejs node
var newDimension = dim.create(from, to, direction);
// make it cube child
cube.add(newDimension);
var animate = function() {
requestAnimationFrame(animate);
// we need to reposition dimension label on each camera change
dim.update(camera);
renderer.render(scene, camera);
};
Let's see into helper classes now.
✔ Dimension line is only visible when camera angle is not too sharp (more than 45°),
class FacingCamera will let you know world plane, that is best facing to the camera. Useful to hide dimensions, which are facing camera with too sharp (acute) angle.
Separate fiddle to play with class FacingCamera can be found here: https://jsfiddle.net/mmalex/56gzn8pL/
class FacingCamera {
constructor() {
// camera looking direction will be saved here
this.dirVector = new THREE.Vector3();
// all world directions
this.dirs = [
new THREE.Vector3(+1, 0, 0),
new THREE.Vector3(-1, 0, 0),
new THREE.Vector3(0, +1, 0),
new THREE.Vector3(0, -1, 0),
new THREE.Vector3(0, 0, +1),
new THREE.Vector3(0, 0, -1)
];
// index of best facing direction will be saved here
this.facingDirs = [];
this.bestFacingDir = undefined;
// TODO: add other facing directions too
// event listeners are collected here
this.cb = {
facingDirChange: []
};
}
check(camera) {
camera.getWorldDirection(this.dirVector);
this.dirVector.negate();
var maxk = 0;
var maxdot = -1e19;
var oldFacingDirs = this.facingDirs;
var facingDirsChanged = false;
this.facingDirs = [];
for (var k = 0; k < this.dirs.length; k++) {
var dot = this.dirs[k].dot(this.dirVector);
var angle = Math.acos(dot);
if (angle > -Math.PI / 2 && angle < Math.PI / 2) {
this.facingDirs.push(k);
if (oldFacingDirs.indexOf(k) === -1) {
facingDirsChanged = true;
}
if (Math.abs(dot) > maxdot) {
maxdot = dot;
maxk = k;
}
}
}
// and if facing direction changed, notify subscribers
if (maxk !== this.bestFacingDir || facingDirsChanged) {
var prevDir = this.bestFacingDir;
this.bestFacingDir = maxk;
for (var i = 0; i < this.cb.facingDirChange.length; i++) {
this.cb.facingDirChange[i]({
before: {
facing: oldFacingDirs,
best: prevDir
},
current: {
facing: this.facingDirs,
best: this.bestFacingDir
}
}, this);
}
}
}
}
✔ Dimension text is HTML element, styled with CSS and positioned with three.js raycasting logic.
class LinearDimension creates an instance of linear dimension with arrows and text label, and controls it.
LinearDimension complete implementation:
class LinearDimension {
constructor(domRoot, renderer, camera) {
this.domRoot = domRoot;
this.renderer = renderer;
this.camera = camera;
this.cb = {
onChange: []
};
this.config = {
headLength: 0.5,
headWidth: 0.35,
units: "mm",
unitsConverter: function(v) {
return v;
}
};
}
create(p0, p1, extrude) {
this.from = p0;
this.to = p1;
this.extrude = extrude;
this.node = new THREE.Object3D();
this.hidden = undefined;
let el = document.createElement("div");
el.id = this.node.id;
el.classList.add("dim");
el.style.left = "100px";
el.style.top = "100px";
el.innerHTML = "";
this.domRoot.appendChild(el);
this.domElement = el;
this.update(this.camera);
return this.node;
}
update(camera) {
this.camera = camera;
// re-create arrow
this.node.children.length = 0;
let p0 = this.from;
let p1 = this.to;
let extrude = this.extrude;
var pmin, pmax;
if (extrude.x >= 0 && extrude.y >= 0 && extrude.z >= 0) {
pmax = new THREE.Vector3(
extrude.x + Math.max(p0.x, p1.x),
extrude.y + Math.max(p0.y, p1.y),
extrude.z + Math.max(p0.z, p1.z));
pmin = new THREE.Vector3(
extrude.x < 1e-16 ? extrude.x + Math.min(p0.x, p1.x) : pmax.x,
extrude.y < 1e-16 ? extrude.y + Math.min(p0.y, p1.y) : pmax.y,
extrude.z < 1e-16 ? extrude.z + Math.min(p0.z, p1.z) : pmax.z);
} else if (extrude.x <= 0 && extrude.y <= 0 && extrude.z <= 0) {
pmax = new THREE.Vector3(
extrude.x + Math.min(p0.x, p1.x),
extrude.y + Math.min(p0.y, p1.y),
extrude.z + Math.min(p0.z, p1.z));
pmin = new THREE.Vector3(
extrude.x > -1e-16 ? extrude.x + Math.max(p0.x, p1.x) : pmax.x,
extrude.y > -1e-16 ? extrude.y + Math.max(p0.y, p1.y) : pmax.y,
extrude.z > -1e-16 ? extrude.z + Math.max(p0.z, p1.z) : pmax.z);
}
var origin = pmax.clone().add(pmin).multiplyScalar(0.5);
var dir = pmax.clone().sub(pmin);
dir.normalize();
var length = pmax.distanceTo(pmin) / 2;
var hex = 0x0;
var arrowHelper0 = new THREE.ArrowHelper(dir, origin, length, hex, this.config.headLength, this.config.headWidth);
this.node.add(arrowHelper0);
dir.negate();
var arrowHelper1 = new THREE.ArrowHelper(dir, origin, length, hex, this.config.headLength, this.config.headWidth);
this.node.add(arrowHelper1);
// reposition label
if (this.domElement !== undefined) {
let textPos = origin.project(this.camera);
let clientX = this.renderer.domElement.offsetWidth * (textPos.x + 1) / 2 - this.config.headLength + this.renderer.domElement.offsetLeft;
let clientY = -this.renderer.domElement.offsetHeight * (textPos.y - 1) / 2 - this.config.headLength + this.renderer.domElement.offsetTop;
let dimWidth = this.domElement.offsetWidth;
let dimHeight = this.domElement.offsetHeight;
this.domElement.style.left = `${clientX - dimWidth/2}px`;
this.domElement.style.top = `${clientY - dimHeight/2}px`;
this.domElement.innerHTML = `${this.config.unitsConverter(pmin.distanceTo(pmax)).toFixed(2)}${this.config.units}`;
}
}
detach() {
if (this.node && this.node.parent) {
this.node.parent.remove(this.node);
}
if (this.domElement !== undefined) {
this.domRoot.removeChild(this.domElement);
this.domElement = undefined;
}
}
}