UNPKG

react-planner

Version:

react-planner is a React Component for plans design. Draw a 2D floorplan and navigate it in 3D mode.

397 lines (289 loc) 15.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _react = require('react'); var _react2 = _interopRequireDefault(_react); var _propTypes = require('prop-types'); var _propTypes2 = _interopRequireDefault(_propTypes); var _reactDom = require('react-dom'); var _reactDom2 = _interopRequireDefault(_reactDom); var _three = require('three'); var Three = _interopRequireWildcard(_three); var _sceneCreator = require('./scene-creator'); var _threeMemoryCleaner = require('./three-memory-cleaner'); var _immutablediff = require('immutablediff'); var _immutablediff2 = _interopRequireDefault(_immutablediff); var _pointerLockNavigation = require('./pointer-lock-navigation'); var _firstPersonControls = require('./libs/first-person-controls'); var _sharedStyle = require('../../shared-style'); var SharedStyle = _interopRequireWildcard(_sharedStyle); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var Viewer3DFirstPerson = function (_React$Component) { _inherits(Viewer3DFirstPerson, _React$Component); function Viewer3DFirstPerson(props) { _classCallCheck(this, Viewer3DFirstPerson); var _this = _possibleConstructorReturn(this, (Viewer3DFirstPerson.__proto__ || Object.getPrototypeOf(Viewer3DFirstPerson)).call(this, props)); _this.width = props.width; _this.height = props.height; _this.stopRendering = false; _this.renderer = window.__threeRenderer || new Three.WebGLRenderer({ preserveDrawingBuffer: true }); window.__threeRenderer = _this.renderer; return _this; } _createClass(Viewer3DFirstPerson, [{ key: 'componentDidMount', value: function componentDidMount() { var _this2 = this; /** Variables for movement control **/ var prevTime = performance.now(); var velocity = new Three.Vector3(); var direction = new Three.Vector3(); var moveForward = false; var moveBackward = false; var moveLeft = false; var moveRight = false; var canJump = false; var catalog = this.context.catalog; var actions = { areaActions: this.context.areaActions, holesActions: this.context.holesActions, itemsActions: this.context.itemsActions, linesActions: this.context.linesActions, projectActions: this.context.projectActions }; var state = this.props.state; var data = state.scene; var canvasWrapper = _reactDom2.default.findDOMNode(this.refs.canvasWrapper); var scene3D = new Three.Scene(); // As I need to show the pointer above all scene objects, I use this workaround http://stackoverflow.com/a/13309722 var sceneOnTop = new Three.Scene(); //RENDERER this.renderer.setClearColor(new Three.Color(SharedStyle.COLORS.white)); this.renderer.setSize(this.width, this.height); // LOAD DATA this.planData = (0, _sceneCreator.parseData)(data, actions, catalog); scene3D.add(this.planData.plan); // CAMERA var aspectRatio = this.width / this.height; var camera = new Three.PerspectiveCamera(45, aspectRatio, 0.1, 300000); sceneOnTop.add(camera); // The pointer is on the camera so I show it above all // Set position for the camera camera.position.set(0, 0, 0); camera.up = new Three.Vector3(0, 1, 0); // HELPER AXIS // let axisHelper = new Three.AxisHelper(100); // scene3D.add(axisHelper); // LIGHT var light = new Three.AmbientLight(0xafafaf); // soft white light scene3D.add(light); // Add another light var pointLight = new Three.PointLight(SharedStyle.COLORS.white, 0.4, 1000); pointLight.position.set(0, 0, 0); scene3D.add(pointLight); // POINTER LOCK document.body.requestPointerLock = document.body.requestPointerLock || document.body.mozRequestPointerLock || document.body.webkitRequestPointerLock; document.body.requestPointerLock(); var _initPointerLock = (0, _pointerLockNavigation.initPointerLock)(camera, this.renderer.domElement), controls = _initPointerLock.controls, pointerlockChangeEvent = _initPointerLock.pointerlockChangeEvent, requestPointerLockEvent = _initPointerLock.requestPointerLockEvent; this.controls = controls; this.pointerlockChangeListener = pointerlockChangeEvent; this.requestPointerLockEvent = requestPointerLockEvent; /* Set user initial position */ var humanHeight = 170; // 170 cm var yInitialPosition = this.planData.boundingBox.min.y + (this.planData.boundingBox.min.y - this.planData.boundingBox.max.y) / 2 + humanHeight; this.controls.getObject().position.set(-50, yInitialPosition, -100); sceneOnTop.add(this.controls.getObject()); // Add the pointer lock controls to the scene that will be rendered on top // Add move controls on the page this.keyDownEvent = function (event) { var moveResult = (0, _firstPersonControls.firstPersonOnKeyDown)(event, moveForward, moveLeft, moveBackward, moveRight, canJump, velocity); moveForward = moveResult.moveForward; moveLeft = moveResult.moveLeft; moveBackward = moveResult.moveBackward; moveRight = moveResult.moveRight; canJump = moveResult.canJump; }; this.keyUpEvent = function (event) { var moveResult = (0, _firstPersonControls.firstPersonOnKeyUp)(event, moveForward, moveLeft, moveBackward, moveRight, canJump); moveForward = moveResult.moveForward; moveLeft = moveResult.moveLeft; moveBackward = moveResult.moveBackward; moveRight = moveResult.moveRight; canJump = moveResult.canJump; }; document.addEventListener('keydown', this.keyDownEvent); document.addEventListener('keyup', this.keyUpEvent); // Add a pointer to the scene var pointer = new Three.Object3D(); pointer.name = 'pointer'; var pointerMaterial = new Three.MeshBasicMaterial({ depthTest: false, depthWrite: false, color: SharedStyle.COLORS.black }); var pointerGeometry1 = new Three.Geometry(); pointerGeometry1.vertices.push(new Three.Vector3(-10, 0, 0)); pointerGeometry1.vertices.push(new Three.Vector3(10, 0, 0)); var linePointer1 = new Three.Line(pointerGeometry1, pointerMaterial); linePointer1.position.z -= 100; var pointerGeometry2 = new Three.Geometry(); pointerGeometry2.vertices.push(new Three.Vector3(0, 10, 0)); pointerGeometry2.vertices.push(new Three.Vector3(0, -10, 0)); var linePointer2 = new Three.Line(pointerGeometry2, pointerMaterial); linePointer2.renderDepth = 1e20; linePointer2.position.z -= 100; var pointerGeometry3 = new Three.Geometry(); pointerGeometry3.vertices.push(new Three.Vector3(-1, 1, 0)); pointerGeometry3.vertices.push(new Three.Vector3(1, 1, 0)); pointerGeometry3.vertices.push(new Three.Vector3(1, -1, 0)); pointerGeometry3.vertices.push(new Three.Vector3(-1, -1, 0)); pointerGeometry3.vertices.push(new Three.Vector3(-1, 1, 0)); var linePointer3 = new Three.Line(pointerGeometry3, pointerMaterial); linePointer3.position.z -= 100; pointer.add(linePointer1); pointer.add(linePointer2); pointer.add(linePointer3); camera.add(pointer); // Add the pointer to the camera // OBJECT PICKING var toIntersect = [this.planData.plan]; var mouseVector = new Three.Vector2(0, 0); var raycaster = new Three.Raycaster(); this.firstPersonMouseDown = function (event) { // First of all I check if controls are enabled if (_this2.controls.enabled) { event.preventDefault(); /* Per avere la direzione da assegnare al raycaster, chiamo il metodo getDirection di PointerLockControls, * che restituisce una funzione che a sua volta prende un vettore, vi scrive i valori degli oggetti * pitch e yaw e lo restituisce */ raycaster.setFromCamera(mouseVector, camera); var intersects = raycaster.intersectObjects(toIntersect, true); if (intersects.length > 0 && !isNaN(intersects[0].distance)) { intersects[0].object.interact && intersects[0].object.interact(); } else { _this2.context.projectActions.unselectAll(); } } }; document.addEventListener('mousedown', this.firstPersonMouseDown, false); this.renderer.domElement.style.display = 'block'; // add the output of the renderer to the html element canvasWrapper.appendChild(this.renderer.domElement); this.renderer.autoClear = false; var render = function render() { if (!_this2.stopRendering) { yInitialPosition = _this2.planData.boundingBox.min.y + humanHeight; var multiplier = 5; var time = performance.now(); var delta = (time - prevTime) / 1000 * multiplier; velocity.x -= velocity.x * 10.0 * delta; velocity.z -= velocity.z * 10.0 * delta; velocity.y -= 9.8 * 100.0 * delta / multiplier; // 100.0 = mass direction.z = Number(moveForward) - Number(moveBackward); direction.x = Number(moveLeft) - Number(moveRight); direction.normalize(); // this ensures consistent movements in all directions if (moveForward || moveBackward) velocity.z -= direction.z * 400.0 * delta; if (moveLeft || moveRight) velocity.x -= direction.x * 400.0 * delta; _this2.controls.getObject().translateX(velocity.x * delta); _this2.controls.getObject().translateY(velocity.y * delta); _this2.controls.getObject().translateZ(velocity.z * delta); if (_this2.controls.getObject().position.y < yInitialPosition) { velocity.y = 0; _this2.controls.getObject().position.y = yInitialPosition; canJump = true; } prevTime = time; // Set light position var controlObjectPosition = _this2.controls.getObject().position; pointLight.position.set(controlObjectPosition.x, controlObjectPosition.y, controlObjectPosition.z); for (var elemID in _this2.planData.sceneGraph.LODs) { _this2.planData.sceneGraph.LODs[elemID].update(camera); } _this2.renderer.clear(); // clear buffers _this2.renderer.render(scene3D, camera); // render scene 1 _this2.renderer.clearDepth(); // clear depth buffer _this2.renderer.render(sceneOnTop, camera); // render scene 2 requestAnimationFrame(render); } }; render(); this.camera = camera; this.scene3D = scene3D; this.sceneOnTop = sceneOnTop; // this.planData = planData; } }, { key: 'componentWillUnmount', value: function componentWillUnmount() { this.stopRendering = true; this.renderer.autoClear = true; document.removeEventListener('mousedown', this.firstPersonMouseDown); document.removeEventListener('keydown', this.keyDownEvent); document.removeEventListener('keyup', this.keyUpEvent); document.removeEventListener('pointerlockchange', this.pointerlockChangeEvent); document.removeEventListener('mozpointerlockchange', this.pointerlockChangeEvent); document.removeEventListener('webkitpointerlockchange', this.pointerlockChangeEvent); this.renderer.domElement.removeEventListener('click', this.requestPointerLockEvent); (0, _threeMemoryCleaner.disposeScene)(this.scene3D); this.scene3D.remove(this.planData.plan); this.scene3D = null; this.planData = null; this.renderer.renderLists.dispose(); } }, { key: 'componentWillReceiveProps', value: function componentWillReceiveProps(nextProps) { var width = nextProps.width, height = nextProps.height; var camera = this.camera, renderer = this.renderer, scene3D = this.scene3D, sceneOnTop = this.sceneOnTop, planData = this.planData; var actions = { areaActions: this.context.areaActions, holesActions: this.context.holesActions, itemsActions: this.context.itemsActions, linesActions: this.context.linesActions, projectActions: this.context.projectActions }; this.width = width; this.height = height; camera.aspect = width / height; camera.updateProjectionMatrix(); if (nextProps.scene !== this.props.state.scene) { var changedValues = (0, _immutablediff2.default)(this.props.state.scene, nextProps.state.scene); (0, _sceneCreator.updateScene)(planData, nextProps.state.scene, this.props.state.scene, changedValues.toJS(), actions, this.context.catalog); } renderer.setSize(width, height); renderer.clear(); // clear buffers renderer.render(scene3D, camera); // render scene 1 renderer.clearDepth(); // clear depth buffer renderer.render(sceneOnTop, camera); // render scene 2 } }, { key: 'render', value: function render() { return _react2.default.createElement("div", { ref: "canvasWrapper" }); } }]); return Viewer3DFirstPerson; }(_react2.default.Component); exports.default = Viewer3DFirstPerson; Viewer3DFirstPerson.propTypes = { state: _propTypes2.default.object.isRequired, width: _propTypes2.default.number.isRequired, height: _propTypes2.default.number.isRequired }; Viewer3DFirstPerson.contextTypes = { areaActions: _propTypes2.default.object.isRequired, holesActions: _propTypes2.default.object.isRequired, itemsActions: _propTypes2.default.object.isRequired, linesActions: _propTypes2.default.object.isRequired, projectActions: _propTypes2.default.object.isRequired, catalog: _propTypes2.default.object };