kitchen-simulator
Version:
It is a kitchen simulator (self-contained micro-frontend).
1,119 lines (1,091 loc) • 65 kB
JavaScript
import _typeof from "@babel/runtime/helpers/esm/typeof";
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
import { convert } from "../../utils/convert-units-lite";
import { fromJS, Map } from 'immutable';
import React, { useState } from 'react';
import * as Three from 'three';
import * as SceneCreator from "../../components/viewer3d/scene-creator";
import { ARROW_COLOR, BASE_CABINET_LAYOUTPOS, OBJTYPE_GROUP, OBJTYPE_MESH, SHADE_DARK_PURPLE_COLOR, SHAPE_SVG_DEPTH, SHAPE_SVG_PADDING, SHAPE_SVG_WIDTH, STATUS_WARNING_COLOR, STATUS_WARNING_LIGHT_COLOR, TALL_CABINET_LAYOUTPOS, UNIT_CENTIMETER, UNIT_INCH, WALL_CABINET_LAYOUTPOS } from "../../constants";
import { Item } from "../../models";
import * as GeomUtils from "./geom-utils";
import { loadGLTF } from "./load-obj";
import { animateDoor, translateDrawer } from "../../utils/helper"; // env Map ///////////////
// env Map ///////////////
var paramsCounter = {
envMap: 'HDR',
roughness: 0.6,
metalness: 0.3,
exposure: 1
// debug: true
};
var params = {
envMap: 'HDR',
roughness: 0.9,
metalness: 0.1,
metalness_glossy: 0.2,
exposure: 1.0
// debug: false
};
var paramsModel = {
envMap: 'HDR',
roughness: 0.9,
metalness: 0.2,
exposure: 1.0
// debug: false
};
// /////////////////////////
var cachedObject = {}; // cached Object for quickly load-3d
// render 2d function//////
export function render2DItem(element, layer, scene, sizeinfo, layoutpos, is_corner, shape_svg) {
var _useState = useState(false),
_useState2 = _slicedToArray(_useState, 2),
hover = _useState2[0],
setHover = _useState2[1];
var x = element.x,
y = element.y,
rotation = element.rotation;
var el_DSN = 'el_DSN',
doorStylesKeys = [];
var _element = element.toJS();
if (_element.doorStyle !== undefined) {
if (_element.doorStyle.doorStyles !== undefined) {
doorStylesKeys = Object.keys(_element.doorStyle.doorStyles);
}
}
if ((doorStylesKeys.includes('euro_length') || doorStylesKeys.includes('euro_width') || doorStylesKeys.includes('euro_shape_svg')) && _element.doorStyle.doorStyles.is_euro_cds) {
el_DSN = _element.doorStyle.door_style_name;
} else {
el_DSN = 'el_DSN';
}
var width, depth, el_euro_length, el_euro_width, el_euro_length_unit, el_euro_width_unit, el_is_euro_cds, el_euro_shape_svg;
if (doorStylesKeys.length > 0) {
el_euro_length = _element.doorStyle.doorStyles.euro_length;
el_euro_width = _element.doorStyle.doorStyles.euro_width;
el_is_euro_cds = _element.doorStyle.doorStyles.is_euro_cds;
el_euro_shape_svg = _element.doorStyle.doorStyles.euro_shape_svg;
}
if (el_euro_length === undefined && el_euro_width === undefined) {
el_DSN = 'el_DSN';
}
if (el_DSN === 'Euro & Frameless') {
// sizeinfo["depth"] = el_euro_length;
// sizeinfo["width"] = el_euro_width;
}
var tempWidth = element.properties.get('width');
var tempDepth = element.properties.get('depth');
width = tempWidth && {
length: tempWidth.get('_length'),
unit: tempWidth.get('_unit')
};
depth = tempDepth && {
length: tempDepth.get('_length'),
unit: tempDepth.get('_unit')
};
var originalWidth = convert(sizeinfo.width).from('in').to('cm');
var originalDepth = convert(sizeinfo.depth).from('in').to('cm');
var newWidth = convert(width.length).from(width.unit).to('cm');
var newDepth = convert(depth.length).from(depth.unit).to('cm');
var padding = convert(SHAPE_SVG_PADDING).from(UNIT_INCH).to(UNIT_CENTIMETER);
var angle = element.rotation + 90;
var textRotation = 0;
if (Math.sin(angle * Math.PI / 180) < 0) {
textRotation = 180;
}
var color = '#eee';
if (layoutpos == BASE_CABINET_LAYOUTPOS) {
color = '#3f8db3';
}
if (layoutpos == TALL_CABINET_LAYOUTPOS) {
color = '#93b3be';
}
if (layoutpos == WALL_CABINET_LAYOUTPOS) {
color = '#48b08dcc';
}
var splitStr = [];
var txtContent = [];
var lineCount = 0; //parseInt(((newWidth) / 8 - 0.51).toFixed(), 10) - 1;
var rowCount = 0; //parseInt((element.type.length / lineCount - 0.51).toFixed(), 10);
// Get type
var type = element.type;
var objSKU = this.obj.sku_number;
if (objSKU.length !== 0) {
var dcId,
doorStyle = element.doorStyle;
if (doorStyle instanceof Map) {
dcId = doorStyle.get('id');
} else {
dcId = doorStyle.id;
}
var skuItem = this.obj.skuArray.find(function (el) {
return el.door_color_id === dcId;
});
if (skuItem !== undefined) {
type = skuItem.sku;
}
}
if (rowCount > 0) {
for (var _x = 0; _x < rowCount; _x++) {
splitStr.push(type.slice(lineCount * _x, lineCount * (_x + 1)));
}
}
splitStr.push(type.slice(lineCount * rowCount));
splitStr.forEach(function (el, key) {
txtContent.push(/*#__PURE__*/React.createElement("text", {
key: 'text' + key,
x: "0",
y: newDepth / 2 - 12,
dy: 16 * key,
transform: "translate(".concat(newWidth / 2, ", ").concat(newDepth / 2 + 5, ") scale(1,-1) rotate(").concat(textRotation, ")")
// textLength={newWidth - 10}
// lengthAdjust="spacingAndGlyphs"
,
style: {
fontWeight: 500,
fontSize: '7px',
textAnchor: 'middle',
fill: '#FFF',
display: 'block'
}
}, el));
});
var style = {
stroke: element.selected ? '#565658' : '#565658',
strokeWidth: '2px',
fill: color
};
// let arrow_style = { stroke: element.selected ? '#0096fd' : null, strokeWidth: "2px", fill: "#84e1ce" };
var rendered = null;
if (shape_svg || el_euro_shape_svg) {
var svg_url, svg_width, svg_depth;
if (typeof shape_svg == 'string' || typeof el_euro_shape_svg == 'string') {
if (el_DSN === 'Euro & Frameless' && el_is_euro_cds) {
svg_url = el_euro_shape_svg;
svg_width = newWidth;
svg_depth = newDepth;
} else {
svg_url = shape_svg;
svg_width = originalWidth;
svg_depth = originalDepth;
}
} else {
// if (el_DSN === "Euro & Frameless" && el_is_euro_cds) {
// // svg_url = el_euro_shape_svg.url;
// } else {
// }
svg_url = shape_svg.url;
svg_width = convert(SHAPE_SVG_WIDTH).from(UNIT_INCH).to(UNIT_CENTIMETER);
svg_depth = convert(SHAPE_SVG_DEPTH).from(UNIT_INCH).to(UNIT_CENTIMETER);
}
var padding_width = padding * newWidth / svg_width;
var padding_depth = padding * newDepth / svg_depth;
rendered = /*#__PURE__*/React.createElement("g", {
onMouseOver: function onMouseOver(event) {
setHover(true);
},
onMouseOut: function onMouseOut(event) {
setHover(false);
},
transform: "translate(".concat(x, ",").concat(y, ")")
}, /*#__PURE__*/React.createElement("g", {
transform: "rotate(".concat(rotation, ")")
}, /*#__PURE__*/React.createElement("g", {
transform: "translate(".concat(-newWidth / 2 - padding_width, ",").concat(-newDepth / 2 - padding_depth, ")")
}, /*#__PURE__*/React.createElement("image", {
preserveAspectRatio: "none",
style: {
pointerEvents: 'none'
},
href: svg_url,
width: "".concat(newWidth + 2 * padding_width),
height: "".concat(newDepth + 2 * padding_depth),
transform: "scale(1, -1)",
x: "0",
y: "".concat(-newDepth - 2 * padding_depth)
}), /*#__PURE__*/React.createElement("rect", {
x: "".concat(padding_width),
y: "".concat(padding_depth),
width: "".concat(newWidth),
height: "".concat(newDepth),
visibility: element.toJS().doorStyle.doorStyles !== undefined ? element.toJS().doorStyle.doorStyles && element.toJS().doorStyle.doorStyles.cds && element.toJS().doorStyle.doorStyles.cds.length != 0 && element.toJS().doorStyle.doorStyles.cds.filter(function (cd) {
return cd.itemID == element.getIn(['itemID']);
}) ? 'hidden' : 'visible' : 'hidden',
style: {
pointerEvents: 'all',
opacity: 0.7,
postion: 'relative'
},
fill: STATUS_WARNING_LIGHT_COLOR,
stroke: STATUS_WARNING_COLOR,
strokeWidth: "2px"
}), /*#__PURE__*/React.createElement("g", {
transform: "translate(".concat(padding_width, ",").concat(padding_depth, ")")
}, txtContent))));
} else {
rendered = /*#__PURE__*/React.createElement("g", {
onMouseOver: function onMouseOver(event) {
setHover(true);
},
onMouseOut: function onMouseOut(event) {
setHover(false);
},
transform: "translate(".concat(x, ",").concat(y, ")")
}, /*#__PURE__*/React.createElement("g", {
transform: "rotate(".concat(rotation, ")")
}, /*#__PURE__*/React.createElement("g", {
transform: "translate(".concat(-newWidth / 2, ",").concat(-newDepth / 2, ")")
}, newDepth > 15 ? [/*#__PURE__*/React.createElement("rect", {
key: "base",
x: "0",
y: "12",
width: newWidth,
height: newDepth - 12,
style: style
}), /*#__PURE__*/React.createElement("polygon", {
key: "door",
style: style,
points: "0,9 ".concat(newWidth, ",9 ").concat(newWidth, ",6 ").concat(newWidth - 5, ",6 ").concat(newWidth - 5, ",3 ").concat(newWidth - 2, ",3 ").concat(newWidth - 2, " 0 ").concat(newWidth - 10, " 0 ").concat(newWidth - 10, ",3 ").concat(newWidth - 7, ",3 ").concat(newWidth - 7, ",6 0 6")
})] : /*#__PURE__*/React.createElement("rect", {
key: "base",
x: "0",
y: "0",
width: newWidth,
height: newDepth,
style: style
}), txtContent)));
}
return rendered;
}
// end of render 2d function /////////////////////////
export function loadTexture(url) {
var texture = new Three.TextureLoader().load(url);
texture.colorSpace = Three.SRGBColorSpace;
texture.wrapS = Three.MirroredRepeatWrapping;
texture.wrapT = Three.MirroredRepeatWrapping;
return texture;
}
var applyTexture = function applyTexture(material, texture, length, height) {
if (texture) {
material.map = texture;
material.needsUpdate = true;
material.map.wrapS = Three.RepeatWrapping;
material.map.wrapT = Three.RepeatWrapping;
material.map.repeat.set(length * 0.01, height * 0.01);
if (texture.normal) {
material.normalMap = loadTexture(texture.normal.uri);
material.normalScale = new Vector2(texture.normal.normalScaleX, texture.normal.normalScaleY);
material.normalMap.wrapS = Three.RepeatWrapping;
material.normalMap.wrapT = Three.RepeatWrapping;
material.normalMap.repeat.set(length * texture.normal.lengthRepeatScale, height * texture.normal.heightRepeatScale);
}
}
};
var assignUVs = function assignUVs(geometry) {
geometry.computeBoundingBox();
var _geometry$boundingBox = geometry.boundingBox,
min = _geometry$boundingBox.min,
max = _geometry$boundingBox.max;
var offset = new Three.Vector3(0 - min.x, 0 - min.y, 0 - min.z);
var range = new Three.Vector3(max.x - min.x, max.y - min.y, max.z - min.z);
geometry.faceVertexUvs[0] = geometry.faces.map(function (face) {
var v1 = geometry.vertices[face.a];
var v2 = geometry.vertices[face.b];
var v3 = geometry.vertices[face.c];
return [new Three.Vector3((v1.x + offset.x) / range.x, (v1.y + offset.y) / range.y, (v1.z + offset.z) / range.z), new Three.Vector3((v2.x + offset.x) / range.x, (v2.y + offset.y) / range.y, (v1.z + offset.z) / range.z), new Three.Vector3((v3.x + offset.x) / range.x, (v3.y + offset.y) / range.y, (v1.z + offset.z) / range.z)];
});
geometry.uvsNeedUpdate = true;
};
/**
* Render 3D Item
* @param {Item} element Rendering item
* @param sizeinfo Dimesion of the item
* @param structure_json Structure of the item such as place holders and meshes, etc
*/
export function render3DItem(element, layer, scene, sizeinfo, structure_json, is_corner) {
var mode = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : null;
if (element.doorStyle.constructor !== Map) {
element = element.set('doorStyle', fromJS(element.doorStyle));
}
if (element.doorStyle.toJS().handle_gltf !== '') {
// Check element has doorHandle
for (var i = 1; i < 10; i++) {
element = element.setIn(['doorStyle', 'doorStyles', 'door_handle_' + i + '_gltf'], element.doorStyle.toJS().handle_gltf);
element = element.setIn(['doorStyle', 'doorStyles', 'fixed_drawer_door_handle_' + i + '_gltf'], element.doorStyle.toJS().handle_gltf);
element = element.setIn(['doorStyle', 'doorStyles', 'drawer_door_handle_' + i + '_gltf'], element.doorStyle.toJS().handle_gltf);
}
}
var width = {
length: sizeinfo.width,
unit: 'in'
};
var depth = {
length: sizeinfo.depth,
unit: 'in'
};
var height = {
length: sizeinfo.height,
unit: 'in'
};
var newWidth = convert(width.length).from(width.unit).to('in');
var newDepth = convert(depth.length).from(depth.unit).to('in');
var newHeight = convert(height.length).from(height.unit).to('in');
var mainName = ''; // to get name structure//
if (element.properties.get('width')) newWidth = element.getIn(['properties', 'width', '_length']);
if (element.properties.get('depth')) newDepth = element.getIn(['properties', 'depth', '_length']);
if (element.properties.get('height')) newHeight = element.getIn(['properties', 'height', '_length']);
var structure = structure_json;
// structure.push({name:'model', url: '/assets/model/DCM.gltf'});
structure.model = '/assets/model/DCM.gltf';
var placeholders = structure.placeholders;
var doorStyles = null;
var color = 0xffffff,
glossness = 1,
handleMaterial = {};
var counterTop = element.counterTop;
if (layer.toJS().counterTop.uri) {
counterTop.uri = layer.toJS().counterTop.uri;
}
if ('name' in element.doorStyle) {
doorStyles = new Map(element.doorStyle.doorStyles);
color = element.doorStyle.color;
glossness = element.doorStyle.glossness;
handleMaterial.metalness = element.doorStyle.metalness;
handleMaterial.roughness = element.doorStyle.roughness;
} else if (element.doorStyle != null && element.doorStyle) {
doorStyles = element.doorStyle.get('doorStyles');
color = element.doorStyle.get('color');
glossness = element.doorStyle.get('glossness');
handleMaterial.metalness = element.doorStyle.get('metalness');
handleMaterial.roughness = element.doorStyle.get('roughness');
}
if (color === undefined) color = '#ffffff';
if (glossness === undefined) glossness = 1;
var tempDoorStyles = doorStyles.toJS();
var tempPlaceholders = structure.tempPlaceholders;
var tPlaceholders = tempPlaceholders.find(function (el) {
return el.id === tempDoorStyles.cabinet_door_style_id;
});
if (tPlaceholders !== undefined) {
placeholders = tPlaceholders.placeholders;
var tempStructure = _objectSpread(_objectSpread({}, tPlaceholders.structure), {}, {
animation: structure.animation,
placeholders: structure.placeholders,
tempPlaceholders: structure.tempPlaceholders,
model: structure.model
});
structure = tempStructure;
}
var userData = structure.animation;
// ///////////////////////////
var onLoadItem = function onLoadItem(object) {
var areaMaterial = new Three.MeshStandardMaterial();
areaMaterial.side = Three.DoubleSide;
// areaMaterial.envMap = textureCube;
if (doorStyles != null) if (doorStyles.get('base') != undefined) {
var normalMap = doorStyles.get('base');
var interiortexture = loadTexture(normalMap);
applyTexture(areaMaterial, interiortexture, 100, 100);
}
var object1 = object;
var newAltitude = element.properties.get('altitude').get('_length');
var newUnit = element.properties.get('altitude').get('_unit') || 'in';
newAltitude = convert(newAltitude).from(newUnit).to(scene.unit);
var _element = element.toJS();
if (!_element.doorStyle.doorStyles.is_euro_cds) {
object1.scale.set(100 * newWidth / sizeinfo.width, 100 * newHeight / sizeinfo.height, 100 * newDepth / sizeinfo.depth);
} else {
object1.scale.set(100, 100, 100);
}
// Normalize the origin of the object
var boundingBox = GeomUtils.baseBox3FromObject(object1);
object1.userData = boundingBox;
var door_animate_offset = {};
var matchStr = /\d_(door[^LR1-9]*)(_[LR])?(_[1-9])?$/;
object1.children.forEach(function (child) {
var door_match = child.name.match(matchStr);
if (door_match && door_match.length > 2) {
var _child$children$;
var rotate_match_text = element.properties.toJS().flip_doorhandle ? 'rotate_L' : 'rotate_R';
if (door_match[2] === '_L') {
rotate_match_text = element.properties.toJS().flip_doorhandle ? 'rotate_R' : 'rotate_L';
}
var isDoubleDoor = door_match && door_match[2] !== undefined;
(_child$children$ = child.children[0]) === null || _child$children$ === void 0 || _child$children$.children.forEach(function (it) {
var _it$name, _it$children;
if (it !== null && it !== void 0 && (_it$name = it.name) !== null && _it$name !== void 0 && _it$name.includes('handle') && (_it$children = it.children) !== null && _it$children !== void 0 && _it$children.length && is_corner === 1 && isDoubleDoor) {
var _child$name, _child$name2, _object1$children, _t0$name, _t0$children;
var tname = child !== null && child !== void 0 && (_child$name = child.name) !== null && _child$name !== void 0 && _child$name.endsWith('_L') ? child.name.replace('_L', '_R') : child === null || child === void 0 || (_child$name2 = child.name) === null || _child$name2 === void 0 ? void 0 : _child$name2.replace('_R', '_L');
var tmp = (object1 === null || object1 === void 0 || (_object1$children = object1.children) === null || _object1$children === void 0 ? void 0 : _object1$children.filter(function (item) {
return (item === null || item === void 0 ? void 0 : item.name) === tname;
})) || [];
var t0 = tmp[0];
var filterStr = t0 !== null && t0 !== void 0 && (_t0$name = t0.name) !== null && _t0$name !== void 0 && _t0$name.endsWith('_L') ? 'rotate_L' : 'rotate_R';
var tposArr = (t0 === null || t0 === void 0 || (_t0$children = t0.children) === null || _t0$children === void 0 || (_t0$children = _t0$children[0]) === null || _t0$children === void 0 || (_t0$children = _t0$children.children) === null || _t0$children === void 0 ? void 0 : _t0$children.filter(function (item) {
var _item$name;
return item === null || item === void 0 || (_item$name = item.name) === null || _item$name === void 0 ? void 0 : _item$name.endsWith(filterStr);
})) || [];
var tpos0 = tposArr[0];
if (it.name.includes('base_end')) {
var _it$position$x, _it$position, _it$position$y, _it$position2, _tpos0$position$x, _tpos0$position;
door_animate_offset[child.name] = {
x: -2 * ((_it$position$x = (_it$position = it.position) === null || _it$position === void 0 ? void 0 : _it$position.x) !== null && _it$position$x !== void 0 ? _it$position$x : 0) - 0.12,
y: (_it$position$y = (_it$position2 = it.position) === null || _it$position2 === void 0 ? void 0 : _it$position2.y) !== null && _it$position$y !== void 0 ? _it$position$y : 0,
z: -((_tpos0$position$x = tpos0 === null || tpos0 === void 0 || (_tpos0$position = tpos0.position) === null || _tpos0$position === void 0 ? void 0 : _tpos0$position.x) !== null && _tpos0$position$x !== void 0 ? _tpos0$position$x : 0) + 0.12,
isCorner: true
};
} else {
var _it$position$x2, _it$position3, _it$position$y2, _it$position4, _tpos0$position$x2, _tpos0$position2;
door_animate_offset[child.name] = {
x: -((_it$position$x2 = (_it$position3 = it.position) === null || _it$position3 === void 0 ? void 0 : _it$position3.x) !== null && _it$position$x2 !== void 0 ? _it$position$x2 : 0) - 0.04,
y: (_it$position$y2 = (_it$position4 = it.position) === null || _it$position4 === void 0 ? void 0 : _it$position4.y) !== null && _it$position$y2 !== void 0 ? _it$position$y2 : 0,
z: 2 * ((_tpos0$position$x2 = tpos0 === null || tpos0 === void 0 || (_tpos0$position2 = tpos0.position) === null || _tpos0$position2 === void 0 ? void 0 : _tpos0$position2.x) !== null && _tpos0$position$x2 !== void 0 ? _tpos0$position$x2 : 0) - 0.01,
isCorner: true
};
}
} else if (it.name.endsWith(rotate_match_text) && door_animate_offset[child.name] == undefined) {
door_animate_offset[child.name] = {
x: it.position.x,
y: it.position.y,
z: it.position.z,
isCorner: false
};
}
});
}
});
object1.userData.door_animate_offset = door_animate_offset;
object1.userData.animation = userData;
var center = [(boundingBox.max.x - boundingBox.min.x) / 2 + boundingBox.min.x, (boundingBox.max.y - boundingBox.min.y) / 2 + boundingBox.min.y, (boundingBox.max.z - boundingBox.min.z) / 2 + boundingBox.min.z];
object1.position.x -= center[0];
object1.position.y -= center[1] - (boundingBox.max.y - boundingBox.min.y) / 2;
object1.position.z -= center[2];
object1.position.y += newAltitude;
if (element.selected) {
// if object is selected
// save object transform info///
var scalevec = new Three.Vector3(object1.scale.x, object1.scale.y, object1.scale.z);
var posVec = new Three.Vector3(object1.position.x, object1.position.y, object1.position.z);
object.scale.set(1 * newWidth / sizeinfo.width, 1 * newHeight / sizeinfo.height, 1 * newDepth / sizeinfo.depth);
object1.position.set(0, 0, 0);
object1.rotation.set(0, 0, 0);
//let box = new Three.BoxHelper(object, 0xffffff);
//box.material.lineWidth = 5;
//box.renderOrder = 1000;
//box.material.depthTest = false;
//object.add(box);
var _boundingBox = GeomUtils.baseBox3FromObject(object);
var max = _boundingBox.max;
var min = _boundingBox.min;
var radius = Math.sqrt((_boundingBox.max.x - _boundingBox.min.x) * (_boundingBox.max.x - _boundingBox.min.x) + (_boundingBox.max.z - _boundingBox.min.z) * (_boundingBox.max.z - _boundingBox.min.z)) / 2;
var moveBox = new Three.BoxGeometry(max.x - min.x, max.y - min.y, max.z - min.z);
// translate Object
var controlGeom = GeomUtils.controlGeom();
// rotate Object //////////
var rotGeom = GeomUtils.rotGeoms(radius + 0.05);
// //////////////////////////////////
// upwards Geometry/////////// Move up Object
var upwardsGeom = GeomUtils.upwardsGeom();
// vertical line - 4 lines around object//////////////////////////
var vLineGeom = new Three.BufferGeometry();
var vertices = [(max.x - min.x) / 2 + min.x, 0, max.z, (max.x - min.x) / 2 + min.x, 0, max.z + 1.3];
vLineGeom.setAttribute('position', new Three.BufferAttribute(new Float32Array(vertices), 3));
var vLineGeom1 = new Three.BufferGeometry();
var vertices1 = [(max.x - min.x) / 2 + min.x, 0, min.z, (max.x - min.x) / 2 + min.x, 0, min.z - 1.3];
vLineGeom1.setAttribute('position', new Three.BufferAttribute(new Float32Array(vertices1), 3));
var vLineGeom2 = new Three.BufferGeometry();
var vertices2 = [max.x, 0, max.z - (max.z - min.z) / 2, max.x + 1.3, 0, max.z - (max.z - min.z) / 2];
vLineGeom2.setAttribute('position', new Three.BufferAttribute(new Float32Array(vertices2), 3));
var vLineGeom3 = new Three.BufferGeometry();
var vertices3 = [min.x, 0, max.z - (max.z - min.z) / 2, min.x - 1.3, 0, max.z - (max.z - min.z) / 2];
vLineGeom3.setAttribute('position', new Three.BufferAttribute(new Float32Array(vertices3), 3));
// ///////////////////////////////////////
// set names of transform object
var rotFillObj = new Three.Mesh(rotGeom.rotFill, new Three.MeshPhongMaterial({
color: new Three.Color(0x000000).convertLinearToSRGB(),
side: Three.DoubleSide,
colorWrite: true
}));
var rotStrokeObj = new Three.Line(rotGeom.rotStroke, new Three.LineBasicMaterial({
color: new Three.Color(0xffffff).convertLinearToSRGB(),
colorWrite: true
}));
rotFillObj.name = 'rotate';
var upObj = new Three.Mesh(upwardsGeom, new Three.MeshBasicMaterial({
color: new Three.Color(0xffffff).convertLinearToSRGB(),
side: Three.DoubleSide
}));
upObj.name = 'transUp';
var mBox = new Three.Mesh(moveBox, new Three.MeshBasicMaterial({
color: new Three.Color(0xdd6699).convertLinearToSRGB(),
side: Three.DoubleSide,
transparent: true,
opacity: 0.4
}));
var _color = new Three.Color(SHADE_DARK_PURPLE_COLOR).convertLinearToSRGB();
var vLine = new Three.Line(vLineGeom, new Three.LineBasicMaterial({
color: _color
}));
var vLine1 = new Three.Line(vLineGeom1, new Three.LineBasicMaterial({
color: _color
}));
var vLine2 = new Three.Line(vLineGeom2, new Three.LineBasicMaterial({
color: _color
}));
var vLine3 = new Three.Line(vLineGeom3, new Three.LineBasicMaterial({
color: _color
}));
vLine.renderOrder = 1;
vLine1.renderOrder = 1;
vLine2.renderOrder = 1;
vLine3.renderOrder = 1;
vLine.material.transparent = true;
vLine1.material.transparent = true;
vLine2.material.transparent = true;
vLine3.material.transparent = true;
vLine.material.depthTest = false;
vLine1.material.depthTest = false;
vLine2.material.depthTest = false;
vLine3.material.depthTest = false;
// translate vector to center of object
var uVec = new Three.Vector3(-posVec.x / scalevec.x, -posVec.y / scalevec.y, -posVec.z / scalevec.z);
vLine.translateY(0.1);
vLine1.translateY(0.1);
vLine2.translateY(0.1);
vLine3.translateY(0.1);
//rotObj.translateOnAxis(uVec, 1);
upObj.translateOnAxis(uVec, 1);
upObj.translateY(max.y - min.y);
mBox.name = 'TransformBox';
mBox.translateOnAxis(new Three.Vector3(uVec.x, uVec.y + (max.y - min.y) / 2, uVec.z), 1);
mBox.scale.set(1.01, 1.01, 1.01);
// other side rotate object
var rotFillObj1 = rotFillObj.clone();
var rotStrokeObj1 = rotStrokeObj.clone();
rotFillObj1.rotateY(Math.PI);
rotStrokeObj1.rotateY(Math.PI);
rotFillObj.translateY(0.1);
rotFillObj1.translateY(0.1);
rotStrokeObj.translateY(0.1);
rotStrokeObj1.translateY(0.1);
// assets Objects group includes rotate objects...
var asrtObj = new Three.Group();
// asrtObj.add(rotFillObj);
// asrtObj.add(rotFillObj1);
// asrtObj.add(rotStrokeObj);
// asrtObj.add(rotStrokeObj1);
//asrtObj.add(upObj);
asrtObj.add(vLine);
asrtObj.add(vLine1);
asrtObj.add(vLine2);
asrtObj.add(vLine3);
mBox.visible = false;
asrtObj.add(mBox);
asrtObj.scale.set(1 / object.scale.x, object.scale.y, 1 / object.scale.z);
//asrtObj.translateY(newAltitude / scalevec.y);
asrtObj.name = 'TransformGizmo';
// add assets Objects Group
object1.add(asrtObj);
// recover objects transform
object1.position.x = posVec.x;
object1.position.y = posVec.y;
object1.position.z = posVec.z;
object1.scale.set(scalevec.x, scalevec.y, scalevec.z);
setTimeout(function () {
SceneCreator.getDistances(layer);
}, 100);
}
var flip_doorhandle = element.properties.get('flip_doorhandle');
if (flip_doorhandle) {
SceneCreator.updateDoorHandleMesh(element, object1, true);
}
object1.traverse(function (obj) {
if (obj.type === OBJTYPE_MESH) {
var name = obj.name;
if (name.match(/_door_.*_glass_/)) {
var material = new Three.MeshPhysicalMaterial({
roughness: 0,
transmission: 1,
thickness: 0.5,
// Add refraction!
transparency: 0.8
});
obj.material = material;
} else if (name.startsWith('sink_')) {
// texture = loadTexture('/assets/img/texture/steel.jpg');
var _material;
// Get color from name
if (name.includes('black') || name.includes('white')) {
var _color2;
if (name.includes('black')) {
_color2 = new Three.Color(0x555555).convertLinearToSRGB();
} else {
_color2 = 0xffffff;
}
_material = new Three.MeshPhysicalMaterial({
roughness: 0.5,
metalness: 0,
// transmission: 1,
transparent: true,
opacity: 1,
thickness: 0.5,
// Add refraction!
// transparency: 0.8,
color: _color2,
side: Three.DoubleSide
});
} else {
// if (name.includes('chrome')) {
_material = new Three.MeshPhysicalMaterial({
roughness: 0.2,
metalness: 1,
reflectivity: 0.5,
color: new Three.Color(0xdddddd).convertLinearToSRGB()
});
}
obj.material = _material;
}
}
if (element.toJS().properties.open_doors) {
var open_doors = element.toJS().properties.open_doors;
var object_match = obj.name.match(/\d_(interior_drawer[^LR1-9]*)(_[LR1-9])?$/) || obj.name.match(matchStr);
if (object_match && object_match.length > 2) {
var isDoor = object_match[1] === 'door';
if (isDoor) {
var offsetData = object1.userData.door_animate_offset[obj.name];
// Open Door
animateDoor(offsetData, obj, open_doors, element.toJS(), is_corner, 'Opened2D');
} else {
// Open Drawer
translateDrawer(element.toJS(), obj, open_doors, 'Opened2D');
}
}
}
});
return object1;
};
// keys in structure
var keys = Object.keys(structure);
// if exist in cached Objects
if (element.type + color + 'doorStyle' + JSON.stringify(doorStyles.toJS()) + element.counterTop.uri in cachedObject) {
var _objGroup = cachedObject[element.type + color + 'doorStyle' + JSON.stringify(doorStyles.toJS())].clone();
return Promise.resolve(onLoadItem(_objGroup.clone()));
}
// base Object/////
var objGroup = null;
var _loadGLTFs = function loadGLTFs(i) {
if (keys[i] === 'animation') {
// if animation info
i++;
return _loadGLTFs(i);
}
if (keys[i] === 'placeholders') {
// if placeholders group
i++;
return _loadGLTFs(i);
}
if (i === keys.length) {
// if end of keys
cachedObject[element.type + color + 'doorStyle' + JSON.stringify(doorStyles.toJS())] = objGroup.clone(); //register to cachedObject
return onLoadItem(cachedObject[element.type + color + 'doorStyle' + JSON.stringify(doorStyles.toJS())].clone());
}
if (keys[i] === 'base') {
// if base Objects
i++;
return _loadGLTFs(i);
}
var phsArray = [];
var placeholderStructure = placeholders[keys[i]];
if (placeholderStructure == undefined || placeholderStructure.length == 0) {
i++;
return _loadGLTFs(i);
}
for (var j = 0; j < placeholderStructure.length; j++) {
var phData = placeholderStructure[j];
var phs = phData.split('/');
var temp = phData.split('/');
// placeholder remake////////////////
for (var k = 0; k < phs.length; k++) {
if (phs[k] in placeholders) {
var placeholderphs = placeholders[phs[k]];
var key = placeholderStructure.length / placeholderphs.length;
phs[k] = placeholderphs[Math.floor(j / key)];
var splitedData = phs[k].split('/');
if (splitedData.length > 1) {
phs[k] = splitedData[splitedData.length - 1];
for (var m = splitedData.length - 2; m >= 0; m--) {
phs.unshift(splitedData[m]);
temp.unshift(splitedData[m]);
}
}
k = -1;
continue;
}
if (phs[k].indexOf('ph') == -1) {
var _url = structure[temp[k - 1]];
if (temp[k - 1] + '_doorStyle' + element.type + 'doorStyle' + JSON.stringify(doorStyles.toJS()) in structure) {
if (structure[temp[k - 1] + '_doorStyle' + element.type + 'doorStyle' + JSON.stringify(doorStyles.toJS())] != null) {
_url = structure[temp[k - 1] + '_doorStyle' + element.type + 'doorStyle' + JSON.stringify(doorStyles.toJS())];
}
}
if (_typeof(_url) == Array) _url = _url[0];
var uData = _url.split('/');
uData = uData[uData.length - 1];
uData = uData.slice(0, -5);
var datas = uData.split('_');
uData = datas[1];
for (var _i = 2; _i < datas.length; _i++) {
uData += '_';
uData += datas[_i];
}
uData = mainName.replace('main', uData);
phs[k] = 'ph_' + uData + '_' + phs[k];
}
}
phsArray.push(phs);
}
var url = structure[keys[i]];
var normalMap = '';
var urlData = url.split('/');
for (var _j = 0; _j < element.submodule.size; _j++) {
var replaceUrlData = element.submodule.get(_j).split('/');
if (urlData.includes(replaceUrlData[replaceUrlData.length - 2])) {
url = element.submodule.get(_j);
break;
}
}
for (var _j2 = 0; _j2 < element.normalMap.size; _j2++) {
var normalMapData = element.normalMap.get(_j2).split('/');
if (urlData.includes(normalMapData[normalMapData.length - 2])) {
normalMap = element.normalMap.get(_j2);
break;
}
}
// replace submodule gltf file
// if (placeholderTree.length > 0) {
if (phsArray.length > 0) {
// let loadUrl = dirName + url;
var loadUrl = url;
if (doorStyles.get(keys[i] + '_gltf') != undefined) {
loadUrl = doorStyles.get(keys[i] + '_gltf');
structure[keys[i] + '_doorStyle' + element.type + 'doorStyle' + JSON.stringify(doorStyles.toJS())] = loadUrl;
} else {
structure[keys[i] + '_doorStyle' + element.type + 'doorStyle' + JSON.stringify(doorStyles.toJS())] = null;
}
return loadGLTF(loadUrl).then(function (object) {
if (normalMap !== '') {
var normalUrl = normalMap.split('.')[0] + '-normal.' + normalMap.split('.')[1];
var t = loadTexture(normalMap);
var _m = loadTexture(normalUrl);
var mat2 = new Three.MeshStandardMaterial({
metalness: glossness === 1 ? params.metalness : params.metalness_glossy,
roughness: glossness || params.roughness
});
mat2.map = t;
mat2.normalMap = _m;
// mat2.envMap = textureCube;
for (var _j3 = 0; _j3 < object.children.length; _j3++) {
if (object.children[_j3].type === OBJTYPE_MESH) {
object.children[_j3].material = mat2;
object.children[_j3].receiveShadow = true;
}
}
}
// set Door Style////
if (doorStyles != null) if (doorStyles.get(keys[i]) != undefined) {
// let normalMap = "catalog/items/doorstyle/" + doorStyles.get(keys[i]);
var _normalMap = doorStyles.get(keys[i]);
var _mat;
if (_normalMap === '') {
var examplecolor = new Three.Color(parseInt(color.slice(1), 16)).convertLinearToSRGB();
_mat = new Three.MeshStandardMaterial({
color: examplecolor,
metalness: glossness === 1 ? params.metalness : params.metalness_glossy,
roughness: glossness || params.roughness
});
} else {
var _t = loadTexture(_normalMap);
_mat = new Three.MeshStandardMaterial({
// NOTE : this is for cabinets (wood) frontface
metalness: 0.1,
roughness: 0.5
// metalness: glossness === 1 ? params.metalness : params.metalness_glossy,
// roughness: glossness || params.roughness
});
_mat.map = _t;
}
// mat2.envMap = textureCube;
for (var _j4 = 0; _j4 < object.children.length; _j4++) {
if (object.children[_j4].type === OBJTYPE_MESH) {
object.children[_j4].material = _mat;
object.children[_j4].receiveShadow = true;
object.children[_j4].castShadow = true;
!object.children[_j4].name.includes('handle') && addEdgesToMesh(object.children[_j4]);
} else if (!object.children[_j4].name.startsWith('ph_') && object.children[_j4].type === OBJTYPE_GROUP) {
object.children[_j4].traverse(function (prim) {
prim.material = _mat;
prim.receiveShadow = true;
});
}
}
} else {
var _mat2 = new Three.MeshStandardMaterial({
metalness: glossness === 1 ? params.metalness : params.metalness_glossy,
roughness: glossness || params.roughness
});
// mat2.envMap = textureCube;
for (var _j5 = 0; _j5 < object.children.length; _j5++) {
if (object.children[_j5].type === OBJTYPE_MESH) {
object.children[_j5].material = _mat2;
object.children[_j5].receiveShadow = true;
}
}
}
for (var _i2 = 0; _i2 < phsArray.length; _i2++) {
var _phs = phsArray[_i2];
var parent = objGroup;
for (var _j6 = 0; _j6 < _phs.length; _j6++) {
var placeholder = _phs[_j6];
for (var _k = 0; _k < ((_parent = parent) === null || _parent === void 0 ? void 0 : _parent.children.length); _k++) {
var _parent;
if (_j6 != _phs.length - 1) {
if (parent.children[_k].name == placeholder) {
parent = parent.children[_k].children[0];
break;
}
} else {
if (parent.children[_k].name == placeholder) {
var tmp = object.clone();
if (placeholder.includes('drawer_door') && placeholder.includes('_handle')) {
tmp.rotateZ(Math.PI / 2);
}
if (placeholder.includes('_handle') && tmp.children[0].type === OBJTYPE_MESH) {
// NOTE: change metalness of handle
tmp.children[0].material.metalness = 1;
// handleMaterial.metalness || 0.2;
tmp.children[0].material.roughness = handleMaterial.roughness || 0.1;
//tmp.children[0].material.map = loadTexture('catalog/areas/area/textures/grass.jpg');
}
parent.children[_k].add(tmp);
}
}
}
}
}
}, function (reason) {
console.log('loadGLTF failed for reason:', reason);
}).then(function () {
i++;
return _loadGLTFs(i);
});
}
};
return loadGLTF(structure['base']).then(function (object) {
object.name = 'MainObject';
object.receiveShadow = true;
objGroup = object;
if (doorStyles != null) if (doorStyles.get('base') != undefined) {
var normalMap = doorStyles.get('base');
if (counterTop.uri === undefined) {
try {
counterTop = counterTop.toJS();
} catch (error) {
console.log(error);
}
}
if (counterTop.uri === undefined && layer.toJS().counterTop.uri !== undefined) {
counterTop.uri = layer.toJS().counterTop.uri;
}
var countTopMap = counterTop.uri;
var interiorMap = doorStyles.get('interior');
var countT = loadTexture(countTopMap);
countT.wrapS = Three.RepeatWrapping;
countT.wrapT = Three.RepeatWrapping;
countT.repeat.set(1, 1);
var examplecolor = new Three.Color(parseInt(color.slice(1), 16)).convertLinearToSRGB();
var mat2 = null,
mat3 = null,
mat4 = null;
if (normalMap === '') {
mat2 = new Three.MeshStandardMaterial({
color: examplecolor,
metalness: glossness === 1 ? params.metalness : params.metalness_glossy,
roughness: glossness || params.roughness
});
} else {
mat2 = new Three.MeshStandardMaterial({
// TODO: changes in metalness and roughness of base_main (cabinet wood)
metalness: 0.1,
roughness: 0.5
// metalness: glossness === 1 ? params.metalness : params.metalness_glossy,
// roughness: glossness || params.roughness
});
}
// mat2.envMap = textureCube;
if (normalMap !== '') {
var t = loadTexture(normalMap);
mat2.map = t;
}
if (normalMap === '') {
mat3 = new Three.MeshStandardMaterial({
// color: examplecolor,
metalness: counterTop.metalness,
roughness: counterTop.roughness
});
} else {
mat3 = new Three.MeshStandardMaterial({
// metalness: counterTop.metalness,
// roughness: counterTop.roughness
metalness: 0.3,
roughness: 0.8
});
}
mat3.map = countT;
// mat3.envMap = textureCube;
mat4 = new Three.MeshStandardMaterial({
metalness: params.metalness,
roughness: params.roughness
});
mat4.map = loadTexture(interiorMap);
for (var j = 0; j < object.children.length; j++) {
if (object.children[j].name.includes('main')) {}
if (object.children[j].name.includes('countertop')) {
object.children[j].material = mat3;
object.children[j].receiveShadow = true;
object.children[j].castShadow = true;
addEdgesToMesh(object.children[j]);
} else if (object.children[j].name.includes('_interior_')) {
object.children[j].material = mat4;
} else if (object.children[j].type === OBJTYPE_MESH) {
object.children[j].material = mat2;
object.children[j].receiveShadow = true;
object.children[j].castShadow = true;
}
}
}
}, function (reason) {
console.log('loadGLTF failed for reason:', reason);
objGroup = GeomUtils.emptyBoxHolder(newWidth, newHeight, newDepth);
}).then(function () {
return _loadGLTFs(0);
});
}
// render 3d appliance function ////////////////////////////////
export function render3DApplianceItem(element, layer, scene, sizeinfo, structure_json) {
var mode = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : null;
var structure = structure_json;
var applianceMaterial = element.applianceMaterial;
if (applianceMaterial.metalness == undefined) applianceMaterial = applianceMaterial.toJS();
var onLoadItem = function onLoadItem(object) {
var newAltitude = element.properties.get('altitude').get('_length');
var newUnit = element.properties.get('altitude').get('_unit') || 'in';
newAltitude = convert(newAltitude).from(newUnit).to(scene.unit);
var newWidth = element.properties.get('width').get('_length');
var newWidthUnit = element.properties.get('width').get('_unit') || 'in';
newWidth = convert(newWidth).from(newWidthUnit).to('in');
var newHeight = element.properties.get('height').get('_length');
var newHeightUnit = element.properties.get('height').get('_unit') || 'in';
newHeight = convert(newHeight).from(newHeightUnit).to('in');
var newDepth = element.properties.get('depth').get('_length');
var newDepthUnit = element.properties.get('depth').get('_unit') || 'in';
newDepth = convert(newDepth).from(newDepthUnit).to('in');
object.scale.set(100 * newWidth / sizeinfo.width, 100 * newHeight / sizeinfo.height, 100 * newDepth / sizeinfo.depth);
// Normalize the origin of the object
var boundingBox = new Three.Box3().setFromObject(object);
object.userData = boundingBox;
var center = [(boundingBox.max.x - boundingBox.min.x) / 2 + boundingBox.min.x, (boundingBox.max.y - boundingBox.min.y) / 2 + boundingBox.min.y, (boundingBox.max.z - boundingBox.min.z) / 2 + boundingBox.min.z];
object.position.x -= center[0];
object.position.y -= center[1] - (boundingBox.max.y - boundingBox.min.y) / 2;
object.position.z -= center[2];
object.position.y += newAltitude;
object.traverse(function (obj) {
if (obj.type === OBJTYPE_MESH) {
var name = obj.name;
var texture,
textureLoader = new Three.TextureLoader();
if (name.includes('_black')) {
obj.material.roughness = 0.4;
obj.material.metalness = 1.0;
obj.material.color = new Three.Color(0, 0, 0);
obj.castShadow = true;
obj.receiveShadow = true;
return object;
} else if (name.includes('_wood')) {
texture = loadTexture('/assets/img/texture/wood.jpg');
} else if (name.includes('_glass')) {
// texture = loadTexture('/assets/img/texture/glass.jpg');
var material = new Three.MeshPhysicalMaterial({
transparent: true,
opacity: 0.5,
roughness: 0,
transmission: 1,
thickness: 0.5,
// Add refraction!
transparency: 0.8
});
obj.material = material;
obj.castShadow = true;
obj.receiveShadow = true;
return object;
} else if (name.includes('_steel')) {
// texture = loadTexture('/assets/img/texture/steel.jpg');
var _material2 = new Three.MeshPhysicalMaterial({
roughness: 0.2,
metalness: 0.5,
reflectivity: 0.5,
color: new Three.Color(0xdddddd).convertLinearToSRGB()
});
obj.material = _material2;
obj.castShadow = true;
obj.receiveShadow = true;
return object;
}
var mat = new Three.MeshStandardMaterial({
metalness: 0.1,
roughness: 0.9,
map: texture
});
obj.material = mat;
}
});
if (element.selected) {
// if object is selected
// save object transform info///
var scalevec = new Three.Vector3(object.scale.x, object.scale.y, object.scale.z);
var posVec = new Three.Vector3(object.position.x, object.position.y, object.position.z);
object.scale.set(1 * newWidth / sizeinfo.width, 1 * newHeight / sizeinfo.height, 1 * newDepth / sizeinfo.depth);
object.position.set(0, 0, 0);
object.rotation.set(0, 0, 0);
var _boundingBox2 = new Three.Box3().setFromObject(object);
var max = _boundingBox2.max;
var min = _boundingBox2.min;
var radius = Math.sqrt((_boundingBox2.max.x - _boundingBox2.min.x) * (_boundingBox2.max.x - _boundingBox2.min.x) + (_boundingBox2.max.z - _boundingBox2.min.z) * (_boundingBox2.max.z - _boundingBox2.min.z)) / 2;
var moveBox = new Three.BoxGeometry(max.x - min.x, max.y - min.y, max.z - min.z);
// translate Object
var controlGeom = GeomUtils.controlGeom();
// ////////////////////////
// rotate Object //////////
var rotGeom = GeomUtils.rotGeoms(radius + 0.05);
// //////////////////////////////////
// upwards Geometry/////////// Move up Object
var upwardsGeom = GeomUtils.upwardsGeom();
// ///////////////////////////////////////
// vertical line - 4 lines around object//////////////////////////
var vLineGeom = new Three.BufferGeometry();
var vertices = [(max.x - min.x) / 2 + min.x, 0, min.z, (max.x - min.x) / 2 + min.x, 0, min.z + 1.3];
vLineGeom.setAttribute('position', new Three.BufferAttribute(new Float32Array(vertices), 3));
var vLineGeom1 = new Three.BufferGeometry();
var vertices1 = [(max.x - min.x) / 2 + min.x, 0, min.z, (max.x - min.x) / 2 + min.x, 0, min.z - 1.3];
vLineGeom1.setAttribute('position', new Three.BufferAttribute(new Float32Array(vertices1), 3));
var vLineGeom2 = new Three.BufferGeometry();
var vertices2 = [max.x, 0, max.z - (max.z - min.z) / 2, max.x + 1.3, 0, max.z - (max.z - min.z) / 2];
vLineGeom1.setAttribute('position', new Three.BufferAttribute(new Float32Array(vertices2), 3));
var vLineGeom3 = new Three.BufferGeometry();
var vertices3 = [min.x, 0, max.z - (max.z - min.z) / 2, min.x - 1.3, 0, max.z - (max.z - min.z) / 2];
vLineGeom1.setAttribute('position', new Three.BufferAttribute(new Float32Array(vertices3), 3));
// ///////////////////////////////////////
// set names of transform object
var rotFillObj = new Three.Mesh(rotGeom.rotFill, new Three.MeshPhongMaterial({
color: 0x000000,
side: Three.DoubleSide