UNPKG

kitchen-simulator

Version:

It is a kitchen simulator (self-contained micro-frontend).

202 lines (198 loc) 8.96 kB
import * as Three from 'three'; import { BoxGeometry, Group, Mesh, MeshBasicMaterial, MeshStandardMaterial, RepeatWrapping, TextureLoader, Vector2 } from 'three'; import { verticesDistance } from "../../utils/geometry"; import { isElevationView } from "../../utils/helper"; import * as SharedStyle from "../../shared-style"; import { LINE_THICKNESS, UNIT_CENTIMETER } from "../../constants"; import { convert } from "../../utils/convert-units-lite"; import ThreeBSP from "../../utils/threeCSG.es6"; var params = { envMap: 'HDR', roughness: 0.1, metalness: 0.1, exposure: 0.1 // debug: false }; var halfPI = Math.PI / 2; /** * Apply a texture to a wall face * @param material: The material of the face * @param texture: The texture to load * @param length: The lenght of the face * @param height: The height of the face */ var applyTexture = function applyTexture(material, texture, length, height) { var loader = new TextureLoader(); if (texture) { material.map = loader.load(texture.uri["default"]); material.map.colorSpace = Three.SRGBColorSpace, material.needsUpdate = true; material.map.wrapS = RepeatWrapping; material.map.wrapT = RepeatWrapping; material.map.repeat.set(length * texture.lengthRepeatScale, height * texture.heightRepeatScale); if (texture.normal) { material.normalMap = loader.load(texture.normal.uri["default"]); material.normalScale = new Vector2(texture.normal.normalScaleX, texture.normal.normalScaleY); material.normalMap.wrapS = RepeatWrapping; material.normalMap.wrapT = RepeatWrapping; material.normalMap.repeat.set(length * texture.normal.lengthRepeatScale, height * texture.normal.heightRepeatScale); } } }; export function buildWall(element, layer, scene, textures, mode) { // Get the two vertices of the wall var vetName0 = element.vertices.get(0); var vetName1 = element.vertices.get(1); // let invert = element.toJS().invert; // As long as its two ending points is same. This is no longer a line. if (vetName0 === vetName1) { return Promise.resolve(null); } var wallColor; layer.lines.forEach(function (data) { wallColor = data.wallColor; }); var vertex0 = layer.vertices.get(vetName0); var vertex1 = layer.vertices.get(vetName1); // Get height and thickness of the wall converting them into the current scene units // let height = element.properties.getIn(['height', 'length']); // let thickness = element.properties.getIn(['thickness', 'length']); var height = convert(layer.ceilHeight).from(layer.unit).to(UNIT_CENTIMETER); var thickness = 2.0; var distance = verticesDistance(vertex0, vertex1); var soulMaterial = new Three.MeshPhongMaterial({ color: new Three.Color(wallColor).convertLinearToSRGB() }); //element.selected ? SharedStyle.MESH_SELECTED : 0xD3D3D3 var soulVertices = []; soulVertices.push(new Three.Vector2(-distance / 2, height / 2)); soulVertices.push(new Three.Vector2(distance / 2, height / 2)); soulVertices.push(new Three.Vector2(distance / 2, -height / 2)); soulVertices.push(new Three.Vector2(-distance / 2, -height / 2)); var soulShape = new Three.Shape(soulVertices); var soulMesh = new Mesh(new BoxGeometry(distance, height, thickness), soulMaterial); if (element.userData.stateMode !== 'MODE_DRAWING_HOLE_3D') { element.holes.forEach(function (holeID) { var holeData = layer.holes.get(holeID); if (holeData !== undefined) { var holeWidth = holeData.properties.getIn(['width', 'length']); var holeHeight = holeData.properties.getIn(['height', 'length']); var holeAltitude = holeData.properties.getIn(['altitude', 'length']); var offset = holeData.offset; // if (vertex0.x > vertex1.x) offset = 1 - offset; // if (invert) offset = 1 - offset; var holeDistance = offset * distance; var holeVertices = []; holeVertices.push(new Three.Vector2(0, holeHeight)); holeVertices.push(new Three.Vector2(holeWidth, holeHeight)); holeVertices.push(new Three.Vector2(holeWidth, 0)); holeVertices.push(new Three.Vector2(0, 0)); for (var i = 0; i < holeVertices.length; i++) { holeVertices[i].x += holeDistance - distance / 2 - holeWidth / 2; holeVertices[i].y -= height / 2 - holeAltitude; } var shapeHole = new Three.Shape(holeVertices); soulShape.holes.push(shapeHole); var holeGeometry = new BoxGeometry(holeWidth, holeHeight, thickness); var holeMesh = new Mesh(holeGeometry); holeMesh.position.x += holeWidth / 2; holeMesh.position.y += holeHeight / 2; holeMesh.position.x += holeDistance - distance / 2 - holeWidth / 2; holeMesh.position.y -= height / 2 - holeAltitude; var wallBSP = new ThreeBSP(soulMesh); var holeBSP = new ThreeBSP(holeMesh); var wallWithHoleBSP = wallBSP.subtract(holeBSP); soulMesh = wallWithHoleBSP.toMesh(soulMaterial); } }); } var cAlpha = new Three.Vector2(vertex0.x - vertex1.x, vertex0.y - vertex1.y).angle(); var soulGeometry = new Three.ShapeGeometry(soulShape); var soul = new Mesh(soulGeometry, soulMaterial); soul.receiveShadow = true; soul.castShadow = true; soul.position.y += height / 2; soul.rotation.y = cAlpha + Math.PI; soul.name = 'soulFace'; soulMesh.position.y += height / 2; soulMesh.rotation.y = cAlpha + Math.PI; soulMesh.name = 'soul'; soulMesh.castShadow = true; var frontMaterial = new MeshStandardMaterial({ color: new Three.Color(wallColor).convertLinearToSRGB(), metalness: 0.1, roughness: 0.9 // envMap: textureCube, }); var backMaterial = new MeshStandardMaterial({ color: new Three.Color(wallColor).convertLinearToSRGB(), metalness: 0.1, roughness: 0.9 // envMap: textureCube, }); applyTexture(frontMaterial, textures[element.properties.get('textureB')], distance, height); applyTexture(backMaterial, textures[element.properties.get('textureB')], distance, height); var frontFace = soul.clone(); frontFace.material = frontMaterial; frontFace.name = 'frontFace'; soulMesh.material = frontMaterial; var merged = new Group(); merged.add(frontFace, soulMesh); // if this wall is front of camera, show/hide if (!isElevationView(mode)) { frontFace.onBeforeRender = function (renderer, scene, camera, geometry, material, group) { var soulMesh = this.parent.getObjectByName('soul'); if (geometry.attributes.normal === undefined) return soulMesh.visible = false; geometry.computeVertexNormals(); var normals = geometry.attributes.normal.array; var pos = new Three.Vector4(0, 0, 0, 1); pos = pos.applyMatrix4(this.matrixWorld); pos = pos.applyMatrix4(camera.matrixWorldInverse); var normal = new Three.Vector4(normals[0], normals[1], normals[2], 0); normal = normal.applyMatrix4(this.matrixWorld); normal = normal.applyMatrix4(camera.matrixWorldInverse); if (soulMesh) { if (pos.dot(normal) >= 0) soulMesh.visible = false;else soulMesh.visible = true; } }; } return Promise.resolve(merged); } export function updatedWall(element, layer, scene, textures, mesh, oldElement, differences, selfDestroy, selfBuild) { var noPerf = function noPerf() { selfDestroy(); return selfBuild(); }; var soul = mesh.getObjectByName('soul'); var frontFace = mesh.getObjectByName('frontFace'); var backFace = mesh.getObjectByName('backFace'); if (differences[0] == 'selected') { if (soul !== undefined) { if (element.selected) { soul.material = new MeshBasicMaterial({ color: new Three.Color(SharedStyle.MESH_SELECTED).convertLinearToSRGB() }); } else { var soulMaterial = frontFace ? frontFace.material : backFace.material; soul.material = soulMaterial; } } } else if (differences[0] == 'properties') { if (differences[1] == 'thickness') { var newThickness = LINE_THICKNESS / 2; var oldThickness = LINE_THICKNESS / 2; // let newThickness = element.getIn(['properties', 'thickness', 'length']); // let oldThickness = oldElement.getIn(['properties', 'thickness', 'length']); var halfNewThickness = newThickness / 2; var texturedFaceDistance = halfNewThickness + 1; var originalThickness = oldThickness / soul.scale.z; var alpha = soul.rotation.y; var xTemp = texturedFaceDistance * Math.cos(alpha - halfPI); var zTemp = texturedFaceDistance * Math.sin(alpha - halfPI); soul.scale.set(1, 1, newThickness / originalThickness); frontFace.position.x = soul.position.x + xTemp; frontFace.position.z = soul.position.z + zTemp; backFace.position.x = soul.position.x - xTemp; backFace.position.z = soul.position.z - zTemp; } else return noPerf(); } else return noPerf(); return Promise.resolve(mesh); }