Cube
I want to make something exactly like this but I can change the amount of cubes in the grid with a variable.(I have no idea how to start)
You will need to add the cubes to a Group, then add that Group to a scene, then centre the Group
Here is one approach of doing this:
const grid = new THREE.Group();
const cubeSize = 0.25;
const rows = 4;
const cols = 4;
const gap = 0.01;
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
for (let z = 0; z < rows; z++) {
const cube = createCube(cubeSize);
const pos = ((cubeSize / 2) * rows) / 2 + gap;
const x = pos * row;
const y = pos * col;
cube.position.set(x, y, pos * z);
grid.add(cube);
}
}
}
scene.add(grid);
Note, this won't scale well the more cubes you add. Since the the total number of objects will be (rows + cols) ^ 3, which will produce a large number of objects. In light of this, you can use InstancedMesh mesh, which I have shown a demo of below.
Demo:
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
canvas {
display: block;
}
<script type="module">
import * as THREE from "https://cdn.jsdelivr.net/npm/three#0.121.1/build/three.module.js";
import { OrbitControls } from "https://cdn.jsdelivr.net/npm/three#0.121.1/examples/jsm/controls/OrbitControls.js";
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
const controls = new OrbitControls(camera, renderer.domElement);
const grid = new THREE.Group();
const cubeSize = 0.25;
const size = 20;
const gap = 0.01;
const geometry = new THREE.BoxBufferGeometry(
cubeSize,
cubeSize,
cubeSize
);
const material = new THREE.MeshNormalMaterial();
const cubes = new THREE.InstancedMesh(
geometry,
material,
Math.pow(size, 3)
);
cubes.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
grid.add(cubes);
scene.add(grid);
const cube = new THREE.Object3D();
const center = (size + gap * (size - 1)) * -0.5;
grid.position.set(center, center, center);
camera.position.z = size * 1.5;
(function animate() {
requestAnimationFrame(animate);
let i = 0;
for (let x = 0; x < size; x++) {
for (let y = 0; y < size; y++) {
for (let z = 0; z < size; z++) {
cube.position.set(x, y, z);
cube.updateMatrix();
cubes.setMatrixAt(i, cube.matrix);
i++;
}
}
}
cubes.instanceMatrix.needsUpdate = true;
controls.update();
renderer.render(scene, camera);
})();
</script>
Related
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 have this scene made with THREE.js. (see code snippet attached in the post)
or https://codepen.io/farisk/pen/jOPgKGQ
Currently its a radiating a circular wave based on the formula in the ‘distance()’ function
As such :
return Math.sqrt(Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2));
I’m wondering if its possible to modify the formula so that I can somehow achieve a ‘X’ or ‘Plus sign (+)’ shaped wave instead?
var once = false;
class App {
init() {
this.stats = new Stats();
this.stats.showPanel(0);
document.body.querySelector('.stats').appendChild(this.stats.domElement);
this.backgroundColor = 0x000000;
this.ambientLightColor = 0xffffff;
this.spotLightColor = 0xff9999;
// this.boxColor = 0x1a63ed;
this.boxColor = 0xffffff;
this.angle = 0;
this.gridSize = 30;
this.ratio = 1.3
this.col = this.gridSize*this.ratio;
this.row = this.gridSize;
this.velocity = .05;
this.boxes = [];
this.amplitude = -7;
this.frequency = 0;
this.waveLength = 242;
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(this.backgroundColor);
this.camera = new THREE.PerspectiveCamera(2, window.innerWidth / window.innerHeight, 1, 10000);
this.camera.position.set(0, 800, 0);
this.addRenderer();
document.body.appendChild(this.renderer.domElement);
this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
this.addAmbientLight();
this.addDirectionalLight();
this.addFloor();
this.addBoxes(this.scene);
this.addGUIControls();
this.animate();
window.addEventListener('resize', this.onResize.bind(this));
}
addDirectionalLight() {
this.directionalLight = new THREE.DirectionalLight(0xffffff, 1);
this.directionalLight.castShadow = true;
this.directionalLight.position.set(0, 1, 0);
this.directionalLight.shadow.camera.far = 10000;
this.directionalLight.shadow.camera.near = -100;
this.directionalLight.shadow.camera.left = -40;
this.directionalLight.shadow.camera.right = 40;
this.directionalLight.shadow.camera.top = 20;
this.directionalLight.shadow.camera.bottom = -20;
this.directionalLight.shadow.camera.zoom = 1;
this.directionalLight.shadow.camera.needsUpdate = true;
const targetObject = new THREE.Object3D();
targetObject.position.set(-50, -82, 40);
this.directionalLight.target = targetObject;
this.scene.add(this.directionalLight);
this.scene.add(this.directionalLight.target);
}
addGUIControls() {
this.gui = new dat.GUI();
this.gui.add(this, 'amplitude', -10, 10);
this.gui.add(this, 'velocity', 0, .5);
this.gui.add(this, 'waveLength', 100, 500);
this.controller = this.gui.add(this, 'gridSize', 24, 150);
this.controller.onFinishChange((value) => {
this.gridSize = Math.floor(value);
this.clearScene();
this.col = this.gridSize*this.ratio;
this.row = this.gridSize;
this.addBoxes(this.scene);
});
}
addRenderer() {
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.shadowMap.enabled = true;
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
this.renderer.setSize(window.innerWidth, window.innerHeight);
}
addAmbientLight() {
const light = new THREE.AmbientLight(this.ambientLightColor, .5);
this.scene.add(light);
}
addSpotLight() {
this.spotLight = new THREE.SpotLight(this.spotLightColor);
this.spotLight.position.set(100, 250, 150);
this.spotLight.castShadow = true;
this.scene.add(this.spotLight);
}
clearScene() {
this.scene.remove(this.mesh);
this.boxes = [];
}
addBoxes(scene) {
const size = 0.05;
const height = 20;
const material = new THREE.MeshLambertMaterial({
color: this.boxColor,
});
const geometry = new THREE.BoxBufferGeometry(size, height, size);
geometry.translate( 0, 2.5, 0 );
this.mesh = this.getBox(geometry, material, this.row * this.col);
this.scene.add(this.mesh);
let ii = 0;
for (let i = 0; i < this.col; i++) {
this.boxes[i] = [];
for (let j = 0; j < this.row; j++) {
const pivot = new THREE.Object3D();
this.boxes[i][j] = pivot;
pivot.scale.set(1, 0.001, 1);
// pivot.position.set(i - this.gridSize*this.ratio * .5, height * .5, j - this.gridSize * .5);
pivot.position.set(i - this.gridSize*this.ratio * .5, height * 0, j - this.gridSize * .5);
pivot.updateMatrix();
this.mesh.setMatrixAt(ii++, pivot.matrix);
}
}
this.mesh.instanceMatrix.needsUpdate = true;
}
drawWave() {
let ii= 0;
for (let i = 0; i < this.col; i++) {
for (let j = 0; j < this.row; j++) {
const distance = this.distance(j, i, this.row * .5, this.col * .5);
const offset = this.map(distance, 0, this.waveLength, -100, 100);
const angle = this.angle + offset ;
if (!once) {
console.log(this.boxes)
once = true
}
this.boxes[i][j].scale.y = this.map(Math.sin(angle), -1, -this.amplitude, 0.001, 1);
this.boxes[i][j].rotation.z = this.map(Math.sin(angle), -1, -this.amplitude, 0.001, 1);
this.boxes[i][j].updateMatrix();
this.mesh.setMatrixAt(ii++, this.boxes[i][j].matrix);
}
}
this.mesh.instanceMatrix.needsUpdate = true;
this.angle -= this.velocity;
}
distance(x1, y1, x2, y2) {
// return Math.sin(x1 - x2)
return Math.sqrt(Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2));
}
map(value, start1, stop1, start2, stop2) {
return (value - start1) / (stop1 - start1) * (stop2 - start2) + start2
}
addFloor() {
const planeGeometry = new THREE.PlaneBufferGeometry(10, 500);
const planeMaterial = new THREE.ShadowMaterial({ opacity:1 });
this.floor = new THREE.Mesh(planeGeometry, planeMaterial);
planeGeometry.rotateX(- Math.PI / 2);
this.floor.position.y = 2;
this.floor.receiveShadow = true;
this.scene.add(this.floor);
}
getBox(geometry, material, count) {
const mesh = new THREE.InstancedMesh(geometry, material, count);
mesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
mesh.castShadow = true;
mesh.receiveShadow = true;
return mesh;
}
addGrid() {
const size = this.col;
const divisions = size;
const gridHelper = new THREE.GridHelper(size, divisions);
gridHelper.position.set(0, 0, 0);
gridHelper.material.opacity = 0;
gridHelper.material.transparent = true;
this.scene.add(gridHelper);
}
animate() {
this.stats.begin();
this.drawWave();
this.controls.update();
this.renderer.render(this.scene, this.camera);
this.stats.end();
requestAnimationFrame(this.animate.bind(this));
}
onResize() {
const ww = window.innerWidth;
const wh = window.innerHeight;
this.camera.aspect = ww / wh;
this.camera.updateProjectionMatrix();
this.renderer.setSize(ww, wh);
}
}
new App().init();
html {
font-family: sans-serif;
}
* {
box-sizing: border-box;
}
body {
background: black;
color: #fff;
font-family: sans-serif;
overflow: hidden;
cursor: -webkit-grab;
cursor: -moz-grab;
padding: 0;
margin: 0;
}
canvas {
width: 100%;
height: 100%;
}
.stats {
opacity: 1;
z-index: 10;
position: absolute;
}
.dg.ac {
position: absolute;
z-index: 10 !important;
}
<main>
<div class="stats"></div>
</main>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/stats.js/r16/Stats.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.2/dat.gui.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js"></script>
"Plus" or "X"
The "Plus" is the easiest to implement. You just need the two distances from the center lines (horizontal and vertical).
const yd = j - this.row / 2; // distance from horizontal
const xd = i - this.col / 2; // distance from vertical
The get the minimum of the absolute values of each distance
const distance = Math.min(Math.abs(yd), Math.abs(xd));
You then use that distance as you did in the original.
Implementation
If you change the function draw to the following this will create the plus that I think you are looking for.
drawWave() {
var ii= 0, x, y;
for (x = 0; x < this.col; x++) {
const bRow = this.boxes[x];
for (y = 0; y < this.row; y++) {
const yd = y - this.row / 2;
const xd = x - this.col / 2;
const distance = Math.min(Math.abs(yd), Math.abs(xd));
const angle = this.angle + this.map(distance, 0, this.waveLength, -100, 100);
const size = this.map(Math.sin(angle), -1, -this.amplitude, 0.001, 1);
bRow[y].scale.y = size;
bRow[y].rotation.z = size;
bRow[y].updateMatrix();
this.mesh.setMatrixAt(ii++, bRow[y].matrix);
}
}
this.mesh.instanceMatrix.needsUpdate = true;
this.angle -= this.velocity;
}
The cross
If you want a cross you need only rotate the x and y by 45 deg. The next function does that.
drawWave() {
const xAx = Math.cos(Math.PI / 4); // Axis 45 deg CW
const xAy = Math.sin(Math.PI / 4);
var ii= 0, x, y;
for (x = 0; x < this.col; x++) {
const bRow = this.boxes[x];
for (y = 0; y < this.row; y++) {
const xx = x - this.col / 2;
const yy = y - this.row / 2;
const xd = xx * xAx - yy * xAy; // rotate
const yd = xx * xAy + yy * xAx;
const distance = Math.min(Math.abs(yd), Math.abs(xd));
const angle = this.angle + this.map(distance, 0, this.waveLength, -100, 100);
const size = this.map(Math.sin(angle), -1, -this.amplitude, 0.001, 1);
bRow[y].scale.y = size;
bRow[y].rotation.z = size;
bRow[y].updateMatrix();
this.mesh.setMatrixAt(ii++, bRow[y].matrix);
}
}
this.mesh.instanceMatrix.needsUpdate = true;
this.angle -= this.velocity;
}
It would be far more efficient if you implemented all the above logic in the vertex shader
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'm back for question two on .points. This time wondering how to change the opacity from 0, to 1 and then back within certain pixel distances from the emitter.
var particleCount = 14,
particles = new THREE.Geometry(),
pMaterial = new THREE.PointsMaterial({
map: new THREE.TextureLoader().load("x.png"),
blending: THREE.multiplyBlending,
flatShading: true,
size: 40,
transparent: true,
depthTest: true,
sizeAttenuation: true,
opacity: 1
});
var particleSystem;
My main confusion is that even though I've given it transparency I can't change the value within the update comp I've made for my emitter.
function update() {
//particleSystem.rotation.y += 0.01;
pCount = particleCount;
while (pCount--) {
particle = particles.vertices[pCount];
(This is where a bunch of validation is for where the points are)
particleSystem.geometry.verticesNeedUpdate = true;
particleSystem.rotation.y += (Math.random()*0.001)
}
Render loop:
renderer.setAnimationLoop(() => {
update();
composer.render(scene, camera);
});
I want to make it fade out and not appear in the scene for 20 or so pixels and then fade in. But I'm not entirely sure on how to change the opacity as particle.opacity += 0.1 won't work.
Edit: I'm also uncertain about Size as I want to do a similar thing with it but from 20 to 40, I could probably base it depending on it's Y cordinate. Anyway; I'm also uncertain how to gradually change that too.
Sorry if this is a obvious answer, duplicate question and any help I get. Any alternate methods of what I've seen is in an alternate structure that I don't understand or in array in which I don't know how to put into what I want.
(Thanks in advance)
The issue is that the opacity and the size is a property of the THREE.PointsMaterial. If the pints should have different sizes it is not sufficient to have a list of different vertices in one THREE.Points. There has to be a list of different THREE.Points with different HREE.PointsMaterials.
Create a list of THREE.Points with different materials:
var texture = new THREE.TextureLoader().load( "..." );
var particleSystemCount = 14;
var particleSystems = [];
for (var i = 0; i < particleSystemCount; ++ i) {
var geometry = new THREE.Geometry();
var pMaterial = new THREE.PointsMaterial({
size: 20,
map: texture,
blending: THREE.AdditiveBlending,
transparent: true,
depthTest: false,
sizeAttenuation: true,
opacity: 0
});
// ...
var points = new THREE.Points(geometry, pMaterial);
scene.add(points);
particleSystems.push(points);
}
So in update the opacity and size can be changed individually:
function update() {
for (var i = 0; i < particleSystems.length; ++ i) {
var points = particleSystems[i];
var material = points.material;
var particle = points.geometry.vertices[0];
// ....
if ( material.size < 40 )
material.size += 0.5;
if ( material.opacity < 1 )
material.opacity += 0.01;
// ....
}
}
var canvas_w = window.innerWidth, canvas_h = window.innerHeight;
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, canvas_w/canvas_h, 1, 1000);
camera.position.set(0, 0, 400);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(canvas_w, window.innerHeight);
document.body.appendChild(renderer.domElement);
window.onresize = function() {
canvas_w = window.innerWidth, canvas_h = window.innerHeight;
renderer.setSize(canvas_w, canvas_h);
camera.aspect = canvas_w/canvas_h;
camera.updateProjectionMatrix();
}
var texture = new THREE.TextureLoader().load("https://threejs.org/examples/textures/sprites/circle.png");
var particleSystemCount = 14;
var particleSystems = [];
for (var i = 0; i < particleSystemCount; ++ i) {
var geometry = new THREE.Geometry();
var pMaterial = new THREE.PointsMaterial({
size: 20,
map: texture,
blending: THREE.AdditiveBlending,
transparent: true,
depthTest: false,
sizeAttenuation: true,
opacity: 0
});
var px = (Math.random() - 0.5) * 100;
var py = (Math.random() - 0.5) * 100 + 200;
var pz = (Math.random() - 0.5) * 100;
var particle = new THREE.Vector3(px, py, pz);
particle.velocity = new THREE.Vector3(0, 0, 0);
geometry.vertices.push(particle);
var points = new THREE.Points(geometry, pMaterial);
scene.add(points);
particleSystems.push(points);
}
function update() {
for (var i = 0; i < particleSystems.length; ++ i) {
var points = particleSystems[i];
var material = points.material;
var particle = points.geometry.vertices[0];
if (particle.y < -200) {
particle.x = (Math.random() - 0.5) * 100;
particle.y = (Math.random() - 0.5) * 100 + 200;
particle.z = (Math.random() - 0.5) * 100;
particle.velocity.y = 0;
material.size = 20;
material.opacity = 0;
}
particle.velocity.y -= Math.random() * .1;
particle.add(particle.velocity);
if ( material.size < 40 )
material.size += 0.25;
if ( material.opacity < 1 )
material.opacity += 0.01;
points.geometry.verticesNeedUpdate = true;
points.rotation.y += (Math.random()*0.001)
}
}
renderer.setAnimationLoop(() => {
update();
renderer.render(scene, camera);
});
body { overflow: hidden; margin: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/99/three.min.js"></script>
Right now, I am trying to make a navigational menu, but to do this, I need to detect what side is clicked by the user. Is there any way to do this with raycasting, or if not, any other way?
Here is my code if you need it:
CodePen Link
The short version is here
var geometry = new THREE.BoxGeometry(200, 200, 200);
var material = new THREE.MeshLambertMaterial(
{
color: 65535,
morphTargets: true
});
for (var i = 0; i < 8; i++)
{
var vertices = [];
for (var v = 0; v < geometry.vertices.length; v++)
{
vertices.push(geometry.vertices[v].clone());
if (v === i)
{
vertices[vertices.length - 1].x *= 2;
vertices[vertices.length - 1].y *= 2;
vertices[vertices.length - 1].z *= 2
}
}
geometry.morphTargets.push(
{
name: "target" + i,
vertices: vertices
})
}
mesh = new THREE.Mesh(geometry, material);
mesh.position.y = 0;
scene.add(mesh);
scene.background = new THREE.Color(15790320);
var params = {
influence1: 1,
influence2: 1,
influence3: 1,
influence4: 1,
influence5: 1,
influence6: 1,
influence7: 1,
influence8: 1
};
var geometry = new THREE.PlaneBufferGeometry(5e3, 5e3);
geometry.rotateX(-Math.PI / 2);
var material = new THREE.MeshBasicMaterial(
{
color: 975132,
overdraw: .5
});
onMouseDown(event) {
this.mouse.x = (event.pageX / window.innerWidth) * 2 - 1;
this.mouse.y = -(event.pageY / window.innerHeight) * 2 + 1;
this.raycaster.setFromCamera(this.mouse, this.camera);
let intersectCube = this.raycaster.intersectObjects( Cube , true );
}
Make a raycaster on your mouse and check for intersections with the Cube or its faces