diff options
| author | Jules Laplace <julescarbon@gmail.com> | 2021-04-05 18:59:19 +0200 |
|---|---|---|
| committer | Jules Laplace <julescarbon@gmail.com> | 2021-04-05 18:59:19 +0200 |
| commit | 503dd94e6cd5f65aa306dfc8e50ecf46c485744d (patch) | |
| tree | 14c477d1c902a491c183dea4de4e625d4dc1e0d3 /frontend/site/projects/museum/stl/STLViewer.js | |
| parent | 236aa3a1294310936e8a6752fed5689c1464225b (diff) | |
add 3d assets
Diffstat (limited to 'frontend/site/projects/museum/stl/STLViewer.js')
| -rw-r--r-- | frontend/site/projects/museum/stl/STLViewer.js | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/frontend/site/projects/museum/stl/STLViewer.js b/frontend/site/projects/museum/stl/STLViewer.js new file mode 100644 index 0000000..365b32d --- /dev/null +++ b/frontend/site/projects/museum/stl/STLViewer.js @@ -0,0 +1,191 @@ +import React, {Component} from 'react'; +import PropTypes from 'prop-types' +import ReactDOM from 'react-dom'; +import * as THREE from 'three'; +import STLLoader from './Loader' +import OrbitControlsModule from 'three-orbit-controls' +// import {ScaleLoader} from 'react-spinners'; + +// const STLLoader = STLLoaderModule(THREE); +const OrbitControls = OrbitControlsModule(THREE); + +class STLViewer extends Component { + static propTypes = { + className: PropTypes.string, + url: PropTypes.string, + file: PropTypes.object, + width: PropTypes.number, + height: PropTypes.number, + backgroundColor: PropTypes.string, + modelMaterial: PropTypes.object, + sceneClassName: PropTypes.string, + onSceneRendered: PropTypes.func, + }; + + static defaultProps = { + backgroundColor: '#EAEAEA', + modelMaterial: { color: '#B92C2C' }, + height: 400, + width: 400, + rotate: true, + orbitControls: true, + sceneClassName: '', + }; + + componentDidMount() { + this.renderModel(this.props); + } + + shouldComponentUpdate(nextProps, nextState) { + if (JSON.stringify(nextProps) === JSON.stringify(this.props)) { + return false + } + return true + } + + componentDidUpdate(prevProps, nextState) { + this.renderModel(prevProps); + } + + componentWillUnmount() { + cancelAnimationFrame(this.animationFrame) + } + + componentDidCatch(error, info) { + console.log(error, info) + } + + renderModel(props) { + let camera, scene, renderer, mesh, distance, controls; + const {url, file, width, height, modelMaterial, backgroundColor, orbitControls, sceneClassName, onSceneRendered} = props; + let xDims, yDims, zDims; + let component = this; + + if (!url) return + + scene = new THREE.Scene(); + distance = 10000; + const directionalLight = new THREE.DirectionalLight(0xddeeff, 0.6); + directionalLight.position.x = 100; + directionalLight.position.y = 100; + directionalLight.position.z = 300; + directionalLight.position.normalize(); + scene.add(directionalLight); + + const directionalLight2 = new THREE.DirectionalLight(0xffffdd, 0.6); + directionalLight2.position.x = -100; + directionalLight2.position.y = 100; + directionalLight2.position.z = -300; + directionalLight2.position.normalize(); + scene.add(directionalLight2); + + const ambientLight = new THREE.AmbientLight(0x404040); // soft white light + scene.add(ambientLight); + + const onLoad = geometry => { + geometry.computeFaceNormals(); + geometry.computeVertexNormals(); + geometry.center(); + + mesh = new THREE.Mesh( + geometry, + new THREE.MeshPhongMaterial(modelMaterial) + ); + + geometry.computeBoundingBox(); + xDims = geometry.boundingBox.max.x - geometry.boundingBox.min.x; + yDims = geometry.boundingBox.max.y - geometry.boundingBox.min.y; + zDims = geometry.boundingBox.max.z - geometry.boundingBox.min.z; + + scene.add(mesh); + + mesh.rotateX(this.props.transform.rotate.x) + mesh.rotateY(this.props.transform.rotate.y) + mesh.rotateZ(this.props.transform.rotate.z) + + camera = new THREE.PerspectiveCamera(30, width / height, 1, distance); + camera.position.set(0, 0, Math.max(xDims * 3, yDims * 3, zDims * 3)); + + scene.add(camera); + + renderer = new THREE.WebGLRenderer({ + preserveDrawingBuffer: true, + antialias: true, + alpha: true, + }); + renderer.setSize(width, height); + renderer.setClearColor(backgroundColor, 0); + renderer.domElement.className = sceneClassName; + + if (orbitControls) { + controls = new OrbitControls(camera, ReactDOM.findDOMNode(component)); + controls.enableKeys = false; + controls.addEventListener('change', orbitRender); + } + + ReactDOM.findDOMNode(this).replaceChild(renderer.domElement, + ReactDOM.findDOMNode(this).firstChild); + + render(); + animate(); + + if (typeof onSceneRendered === "function") { + onSceneRendered(ReactDOM.findDOMNode(renderer.domElement)) + } + }; + + const onProgress = (xhr) => { + if (xhr.lengthComputable) { + let percentComplete = xhr.loaded / xhr.total * 100; + } + }; + + const loader = new STLLoader(); + + if (file) { + loader.loadFile(file, onLoad, onProgress); + } else { + loader.load(url, onLoad, onProgress); + } + + const render = () => { + renderer.render(scene, camera); + }; + + const orbitRender = () => { + render(); + }; + + const animate = () => { + if (!mesh) return + this.animationFrame = requestAnimationFrame(animate) + mesh.rotateZ(Math.PI * 0.001) + render() + } + } + + render() { + return ( + <div + className={this.props.className} + style={{ + position: 'fixed', + width: Math.round(this.props.width) + "px", + height: Math.round(this.props.height) + "px", + overflow: 'hidden', + ...this.props.style, + }} + > + <div style={{ + height: '100%', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + }}> + </div> + </div> + ); + }; +}; + +export default STLViewer;
\ No newline at end of file |
