I am doing this ThreeJS project in Angular 7 hence not sure whether I can put it in CodePen or JSFiddle.
If required I can host this app in public URL.
Desired Result:
User will be supplying the coordinates of the affected area in the container and the same should be highlighted as demonstrated in this image. It is also possible to highlight half of the cell in a row.
If any side is not affected then the grid lines can be turned of on that side.
The below is what I have done so far. You can see all // commented lines. I genuinely attempted to achieve the above desired result.
import { Component, OnInit, ElementRef, ViewChild, HostListener, AfterViewInit } from '@angular/core';
import * as OrbitControls from 'three-orbitcontrols';
import * as THREE from 'three';
import { MTLLoader, OBJLoader } from 'three-obj-mtl-loader';
import * as TWEEN from '@tweenjs/tween.js';
import { Color } from 'three';
import { TouchSequence } from 'selenium-webdriver';
@Component({
selector: 'app-myfirstscene',
templateUrl: './myfirstscene.component.html',
styleUrls: ['./myfirstscene.component.css']
})
export class MyfirstsceneComponent implements OnInit, AfterViewInit {
private container: HTMLElement;
@ViewChild('container') elementRef: ElementRef;
private scene: THREE.Scene;
private camera: THREE.PerspectiveCamera;
private renderer: THREE.WebGLRenderer;
public controls: OrbitControls;
private mtlLoader: MTLLoader;
private loader: OBJLoader;
public objects = [];
public objectMaterials = THREE.Material;
public container_object: THREE.Object3D;
DURATION = 100;
DEFAULT_CAMERA_POSITION = 800;
DEFAULT_CONTAINER_SIZE = 40;
CONSTANT_DISTANCE = 250;
MAX_DISTANCE = 1350;
MIN_DISTANCE = 650;
minWheel = 0;
maxWheel = 70;
SCALE_RATIO = {
z: 0.949,
x: {
size20: 1.02,
size40: 1.89,
}
};
FACE_MASKS = {
FRONT: 0x01,
BACK: 0x02,
LEFT: 0x04,
RIGHT: 0x08,
TOP: 0x10,
BOTTOM: 0x12
};
constructor() { }
ngOnInit() {
this.container = this.elementRef.nativeElement;
this.init();
this.animate();
// this.show_container_side(this.FACE_MASKS.FRONT);
this.show_container_side(this.FACE_MASKS.FRONT);
}
ngAfterViewInit() {
}
init() {
const screen = {
width: 800,
height: 600
},
view = {
angle: 45,
aspect: screen.width / screen.height,
near: 0.1,
far: 1000
};
this.camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 2000);
this.camera.position.x = 1000;
// this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000);
// // this.camera.position.z = 250;
// // this.camera.position.x = -1000;
// // this.camera.position.z = 1000;
// this.camera.position.x = -1000;
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color('#EEEEEE');
const ambientLight = new THREE.AmbientLight(0xcccccc, 0.4);
// this.scene.add( ambientLight );
const pointLight = new THREE.PointLight(0xffffff, 0.8);
this.camera.add(pointLight);
this.scene.add(this.camera);
this.renderer = new THREE.WebGLRenderer({ alpha: true });
this.mtlLoader = new MTLLoader();
this.loader = new OBJLoader();
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.renderer.setClearColor(0xEEEEEE);
// new THREE.MTLLoader()
// .setPath("model/")
// .load("apple-watch.mtl", function(materials) {
// materials.preload();
// new THREE.OBJLoader()
// .setMaterials(materials)
// .setPath("model/")
// .load(
// "latest-watch.obj",
// function(object) {
// object3d = object;
// scene.add(object3d);
this.container.appendChild(this.renderer.domElement);
// group
const group = new THREE.Group();
this.scene.add( group );
// this.mtlLoader
// .setPath('assets/models/')
// .load('container.mtl', (mtls) => {
// mtls.preload();
// this.loader
// .setPath('assets/models/')
// .setMaterials(mtls)
// .load('ContainerOBJ', (object) => {
// object.traverse(function (child) {
// if (child instanceof THREE.Mesh) {
// // child.material.map = texture;
// child.castShadow = true;
// child.receiveShadow = true;
// // child.material.color.setHex(0xd3d3ba);
// }
// });
// // object.lookAt(0, 1000, -1);
// // object.position.z = - 95;
// // object.position.set( 0, 1000, -1 );
// this.scene.add(object);
// object.scale.z = 0.949;
// object.scale.x = 1;
// console.log('object loaded');
// this.container_object = object;
// console.log(this.container_object);
// }
// );
this.loader
.setPath('assets/models/')
.load('ContainerOBJ.obj', (object) => {
// geometry.computeBoundingBox();
// var bb = geometry.boundingBox;
// var object3DWidth = bb.max.x - bb.min.x;
// var object3DHeight = bb.max.y - bb.min.y;
// var object3DDepth = bb.max.z - bb.min.z;
// box helper to see the extend of the volume
// object.computeBoundingBox();
// const bb = object.boundingBox;
// const object3DWidth = bb.max.x - bb.min.x;
// const object3DHeight = bb.max.y - bb.min.y;
// const object3DDepth = bb.max.z - bb.min.z;
// console.log('bounding box');
// console.log(bb);
// const vct = new THREE.Vector3();
// const boxsize = new THREE.Box3().setFromObject(object);
// const size = new THREE.Box3().setFromObject(object).getSize(vct);
// const geometry = new THREE.BoxBufferGeometry(size.x, size.y, size.z);
// const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
// const cube = new THREE.Mesh(geometry, material);
// cube.visible = false;
// const box = new THREE.BoxHelper(cube);
// this.scene.add(box);
// box.applyMatrix(object.matrix);
// this.scene.add(cube);
// new THREE.MeshBasicMaterial( {color: 0xffff00, side: THREE.DoubleSide} );
// const cubeMaterials =
// [
// new THREE.LineBasicMaterial( {color: 0xffff00, side: THREE.DoubleSide} ), // RIGHT
// new THREE.LineBasicMaterial( {color: 0xffff00, side: THREE.DoubleSide} ), // LEFT
// new THREE.LineBasicMaterial( {color: 0xffff00, side: THREE.DoubleSide} ), // TOP
// new THREE.LineBasicMaterial( {color: 0xffff00, side: THREE.DoubleSide} ), // BOTTOM
// new THREE.LineBasicMaterial( {color: 0xffff00, side: THREE.DoubleSide} ), // FRONT
// new THREE.LineBasicMaterial( {color: 0xffff00, side: THREE.DoubleSide} ) // BACK
// ];
// const cubeMaterials =
// [
// new THREE.MeshBasicMaterial({new THREE.BoxBufferGeometry(16, 16), side: THREE.DoubleSide }), //RIGHT
// // new THREE.MeshBasicMaterial({new THREE.TextureLoader().load(''), side: THREE.DoubleSide}), //LEFT
// // new THREE.MeshBasicMaterial({new THREE.TextureLoader().load(''), side: THREE.DoubleSide}), //TOP
// // new THREE.MeshBasicMaterial({new THREE.TextureLoader().load(''), side: THREE.DoubleSide}), //BOTTOM
// // new THREE.MeshBasicMaterial({new THREE.TextureLoader().load(''), side: THREE.DoubleSide}), //FRONT
// // new THREE.MeshBasicMaterial({new THREE.TextureLoader().load(''), side: THREE.DoubleSide}) //BACK
// ];
// const material = new THREE.MeshFaceMaterial( cubeMaterials );
object.traverse(function (child) {
if (child instanceof THREE.Mesh) {
// child.material = material;
child.castShadow = true;
child.receiveShadow = true;
}
});
// object.position.z = - 95;
// object.position.set( 0, 1000, -1 );
// this.scene.add(object);
group.add(object);
object.scale.z = 0.949;
object.scale.x = 1;
console.log('object loaded');
this.container_object = object;
// object.material.wireframe = true;
const gridHelper = new THREE.GridHelper( 200, 10, 0x000000, 0x000000 );
group.add( gridHelper );
// const wireframe = new THREE.WireframeHelper( object, 0x00ff00 );
// this.scene.add( wireframe );
// const grid = new THREE.GridHelper( 200 , 16 );
// this.scene.add( grid );
// grid.geometry.rotateX( Math.PI / 2 );
// const vector = new THREE.Vector3( 1, 1, 1 );
// grid.lookAt( vector );
// const gridHelper = new THREE.GridHelper(50, 10); // 500 is grid size, 20 is grid step
// // gridHelper.position = new THREE.Vector3(0, 1000, 0);
// // gridHelper.rotation = new THREE.Euler(0, 0, 0);
// this.scene.add(gridHelper);
// const gridHelper2 = gridHelper.clone();
// gridHelper2.rotation = new THREE.Euler(Math.PI / 2, 0, 0);
// this.scene.add(gridHelper2);
// const gridHelper3 = gridHelper.clone();
// // gridHelper3.rotation = new THREE.Euler(Math.PI / 2, 0, Math.PI / 2);
// this.scene.add(gridHelper3);
// const geometry = new THREE.BoxBufferGeometry(-100, 0, -100, 0 );
// const material = new THREE.MeshBasicMaterial( {color: 0xffff00, side: THREE.DoubleSide} );
// const plane = new THREE.Mesh( geometry, material );
// this.scene.add( plane );
// plane.position.setX(-250);
// plane.position.setY(0);
// const mesh = new THREE.Mesh( geometry, material );
// this.scene.add( mesh );
// const geo = new THREE.EdgesGeometry( mesh.geometry ); // or WireframeGeometry
// const mat = new THREE.LineBasicMaterial( { color: 0xffffff, linewidth: 2 } );
// const wireframe = new THREE.LineSegments( geo, mat );
// mesh.add( wireframe );
// // create a blue LineBasicMaterial
// const material = new THREE.LineBasicMaterial({ color: 0x0000ff });
// const geometry = new THREE.Geometry();
// geometry.vertices.push(new THREE.Vector3(0, 0, 0));
// geometry.vertices.push(new THREE.Vector3(0, 10, 0));
// geometry.vertices.push(new THREE.Vector3(10, 0, 0));
// const line = new THREE.Line(geometry, material);
// this.scene.add( line );
// const geom = new THREE.Geometry();
// const v1 = new THREE.Vector3(0, 0, 0);
// const v2 = new THREE.Vector3(0, 100, 0);
// const v3 = new THREE.Vector3(0, 100, 0);
// geom.vertices.push(v1);
// geom.vertices.push(v2);
// geom.vertices.push(v3);
// geom.faces.push(new THREE.Face3(0, 1, 2));
// const objectMesh = new THREE.Mesh(geom, new THREE.MeshNormalMaterial());
// objectMesh.rotation.x = Math.PI / 2;
// this.scene.add(objectMesh);
// this.render();
// this.show_container_side(this.FACE_MASKS.BACK);
});
console.log('init completed rendered');
this.addControls();
this.createMesh();
}
render() {
this.renderer.render(this.scene, this.camera);
}
// render() {
// const self: MyfirstsceneComponent = this;
// requestAnimationFrame(this.render.bind(self));
// self.renderer.render(self.scene, self.camera);
// self.animate();
// // this.show_container_side(this.FACE_MASKS.FRONT);
// // (function render() {
// // requestAnimationFrame(this.render.bind(this));
// // self.renderer.render(self.scene, self.camera);
// // self.animate();
// // }());
// }
public addControls() {
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
// this.controls.rotateSpeed = 1.0;
// this.controls.zoomSpeed = 1.2;
this.controls.enableDamping = true;
this.controls.dampingFactor = 0.25;
this.controls.enableZoom = true;
this.controls.enablePan = false;
this.controls.addEventListener('change', this.render.bind(this));
console.log('addControls');
}
@HostListener('document:keypress', ['$event'])
public onKeyPress(event: KeyboardEvent) {
console.log('onKeyPress: ' + event.key);
}
@HostListener('document:mousedown', ['$event'])
public onMouseDown(event: MouseEvent) {
console.log('onMouseDown');
event.preventDefault();
// // Example of mesh selection/pick:
// const raycaster = new THREE.Raycaster();
// const mouse = new THREE.Vector3();
// mouse.x = (event.clientX / this.renderer.domElement.clientWidth) * 2 - 1;
// mouse.y = - (event.clientY / this.renderer.domElement.clientHeight) * 2 + 1;
// raycaster.setFromCamera(mouse, this.camera);
// const obj: THREE.Object3D[] = [];
// this.findAllObjects(obj, this.scene);
// const intersects = raycaster.intersectObjects(obj);
// // const colorArray = intersects[0].object.geometry.attributes.color.array,
// const faceIndices = ['a', 'b', 'c', 'd'];
// const face = intersects[0].face;
// const numberOfSides = (face instanceof THREE.Face3) ? 3 : 4;
// console.log('Number of sides' + numberOfSides);
// // assign color to each vertex of current face
// for (let j = 0; j < numberOfSides; j++) {
// const vertexIndex = face[faceIndices[j]];
// // initialize color variable
// const clr = new THREE.Color(0xffffff);
// clr.setRGB(Math.random(), 0, 0);
// face.vertexColors[j] = clr;
// }
// // const color = new THREE.Color( 0xffffff );
// // color.setRGB( Math.random(), 0, 0 );
// // face.vertexColors[0] = color;
// // colorArray[face.a] = 1;
// // colorArray[face.a + 1] = 0;
// // colorArray[face.a + 2] = 0;
// // colorArray[face.b] = 1;
// // colorArray[face.b + 1] = 0;
// // colorArray[face.b + 2] = 0;
// // colorArray[face.c] = 1;
// // colorArray[face.c + 1] = 0;
// // colorArray[face.c + 2] = 0;
// // intersects[0].object.geometry.attributes.color.needsUpdate = true;
// console.log('Scene has ' + obj.length + ' objects');
// console.log(intersects.length + ' intersected objects found');
// intersects.forEach((i) => {
// console.log(i.object); // do what you want to do with object
// });
// this.show_container_side(this.FACE_MASKS.BOTTOM);
}
private findAllObjects(pred: THREE.Object3D[], parent: THREE.Object3D) {
// NOTE: Better to keep separate array of selected objects
if (parent.children.length > 0) {
parent.children.forEach((i) => {
pred.push(i);
this.findAllObjects(pred, i);
});
}
}
public onMouseUp(event: MouseEvent) {
console.log('onMouseUp');
}
animate() {
TWEEN.update();
requestAnimationFrame(this.animate.bind(this));
this.controls.update();
this.render();
}
createMesh() {
const geometry = new THREE.BoxBufferGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cubeA = new THREE.Mesh(geometry, material);
cubeA.position.set(100, 100, 0);
const cubeB = new THREE.Mesh(geometry, material);
cubeB.position.set(-100, -100, 0);
// create a group and add the two cubes
// These cubes can now be rotated / scaled etc as a group
const group = new THREE.Group();
group.add(cubeA);
group.add(cubeB);
this.scene.add(group);
}
show_container_side(side) {
// document.getElementById('dismiss-button').style.display = 'block';
const face_vector = { x: 0, y: 0, z: 0 };
switch (side) {
case this.FACE_MASKS.LEFT:
face_vector.z = 1;
break;
case this.FACE_MASKS.RIGHT:
face_vector.z = -1;
break;
case this.FACE_MASKS.BACK:
face_vector.x = -1;
break;
case this.FACE_MASKS.FRONT:
face_vector.x = 1;
break;
case this.FACE_MASKS.TOP:
face_vector.y = 1;
break;
case this.FACE_MASKS.BOTTOM:
face_vector.y = -1;
break;
}
const selected_side_vector = face_vector;
console.log('selected side of the vector' + face_vector + ' side ' + side);
const current_pos = { x: this.controls.object.position.x, y: this.controls.object.position.y, z: this.controls.object.position.z };
const target_pos = {
x: face_vector.x * this.DEFAULT_CAMERA_POSITION, y: face_vector.y * this.DEFAULT_CAMERA_POSITION,
z: face_vector.z * this.DEFAULT_CAMERA_POSITION
};
const tween = this.get_rotation_tween(current_pos, target_pos, this.DURATION);
// this.controls.disableAutoRotate();
if (this.controls.AutoRotate) {
this.controls.autoRotate = false;
}
tween.start();
// const event = new CustomEvent('show_option_panel', {detail: side} );
// (document)[0].dispatchEvent( event );
console.log('showing ' + side);
}
get_rotation_tween(current, target, duration_time) {
console.log('get_rotation_tween');
const tween = new TWEEN.Tween(current).to(target, duration_time);
tween.easing(TWEEN.Easing.Quartic.Out);
tween.onUpdate(() => {
// this.controls.update();
this.controls.object.position.set(current.x, current.y, current.z);
// this.controls.object.position.set(0, 1000, 1000);
// this.controls.object.position.set(target.x, target.y, target.z);
// this.container_object.lookAt(targe)
// console.log(this.controls);
console.log('current');
console.log(current);
console.log('target');
console.log(target);
});
return tween;
}
}