Fix and enable 3D viewer: #4035

Fix 3d viewer styles

Fix minor warnings from three.js

Remove deprecated themes

Fix #4074
This commit is contained in:
btzr-io 2020-04-25 03:24:42 -05:00 committed by Sean Yesmunt
parent ae3c8d5221
commit e4d822f818
6 changed files with 79 additions and 154 deletions

View file

@ -2,6 +2,7 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import FileRender from 'component/fileRender'; import FileRender from 'component/fileRender';
import LoadingScreen from 'component/common/loading-screen'; import LoadingScreen from 'component/common/loading-screen';
import { NON_STREAM_MODES } from 'constants/file_render_modes';
type Props = { type Props = {
isPlaying: boolean, isPlaying: boolean,
@ -14,9 +15,18 @@ type Props = {
}; };
export default function FileRenderInline(props: Props) { export default function FileRenderInline(props: Props) {
const { isPlaying, fileInfo, uri, streamingUrl, triggerAnalyticsView, claimRewards } = props; const { isPlaying, fileInfo, uri, streamingUrl, triggerAnalyticsView, claimRewards, renderMode } = props;
const [playTime, setPlayTime] = useState(); const [playTime, setPlayTime] = useState();
const isReadyToPlay = streamingUrl || (fileInfo && fileInfo.completed); const isReadyToView = fileInfo && fileInfo.completed;
const isReadyToPlay = streamingUrl || isReadyToView;
// Render if any source is ready
let renderContent = isReadyToPlay;
// Force non-streaming content to wait for download
if (NON_STREAM_MODES.includes(renderMode)) {
renderContent = isReadyToView;
}
useEffect(() => { useEffect(() => {
if (isPlaying) { if (isPlaying) {
@ -39,5 +49,5 @@ export default function FileRenderInline(props: Props) {
return null; return null;
} }
return isReadyToPlay ? <FileRender uri={uri} /> : <LoadingScreen status={__('Preparing your content')} />; return renderContent ? <FileRender uri={uri} /> : <LoadingScreen status={__('Preparing your content')} />;
} }

View file

@ -1,7 +1,6 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import * as dat from 'dat.gui'; import * as dat from 'dat.gui';
import classNames from 'classnames';
import LoadingScreen from 'component/common/loading-screen'; import LoadingScreen from 'component/common/loading-screen';
// ThreeJS // ThreeJS
@ -52,14 +51,13 @@ const ThreeLoader = ({ fileType = null, downloadPath = null }, renderModel, mana
}; };
type viewerTheme = { type viewerTheme = {
gridColor: string, showFog: boolean,
groundColor: string, gridColor: number,
backgroundColor: string, backgroundColor: number,
centerLineColor: string, centerLineColor: number,
}; };
type Props = { type Props = {
theme: string,
source: { source: {
fileType: string, fileType: string,
downloadPath: string, downloadPath: string,
@ -131,26 +129,15 @@ class ThreeViewer extends React.PureComponent<Props, State> {
constructor(props: Props) { constructor(props: Props) {
super(props); super(props);
const { theme } = this.props;
// Object defualt color // Object defualt color
this.materialColor = '#44b098'; this.materialColor = 0xffffff;
// Viewer themes // Default viewer Theme
this.themes = { this.theme = {
dark: { showFog: true,
gridColor: '#414e5c', gridColor: 0x414e5c,
groundColor: '#13233C', centerLineColor: 0x7f8c8d,
backgroundColor: '#13233C', backgroundColor: 0x0b0c0d,
centerLineColor: '#7f8c8d',
},
light: {
gridColor: '#7f8c8d',
groundColor: '#DDD',
backgroundColor: '#EEE',
centerLineColor: '#2F2F2F',
},
}; };
// Select current theme
this.theme = this.themes[theme] || this.themes.light;
// State // State
this.state = { this.state = {
error: null, error: null,
@ -212,29 +199,27 @@ class ThreeViewer extends React.PureComponent<Props, State> {
}); });
// Clean up controls // Clean up controls
if (this.controls) this.controls.dispose(); if (this.controls) this.controls.dispose();
// It's unclear if we need this: // Clean up scene
if (this.scene) this.scene.dispose();
if (this.renderer) { if (this.renderer) {
this.renderer.context = null;
this.renderer.domElement = null;
this.renderer.renderLists.dispose(); this.renderer.renderLists.dispose();
this.renderer.forceContextLoss();
this.renderer.dispose(); this.renderer.dispose();
this.renderer = null;
} }
// Stop animation // Stop animation
cancelAnimationFrame(this.frameID); cancelAnimationFrame(this.frameID);
// Destroy GUI Controls // Destroy GUI Controls
if (this.gui) this.gui.destroy(); if (this.gui) this.gui.destroy();
// Empty objects
this.grid = null;
this.mesh = null;
this.renderer = null;
this.material = null;
this.geometry = null;
this.bufferGeometry = null;
} }
} }
// Define component types // Define component types
theme: viewerTheme; theme: viewerTheme;
themes: { dark: viewerTheme, light: viewerTheme }; materialColor: number;
materialColor: string;
// Refs // Refs
viewer: ?HTMLElement; viewer: ?HTMLElement;
guiContainer: ?HTMLElement; guiContainer: ?HTMLElement;
@ -265,10 +250,10 @@ class ThreeViewer extends React.PureComponent<Props, State> {
transformGroup(group: any) { transformGroup(group: any) {
ThreeViewer.fitMeshToCamera(group); ThreeViewer.fitMeshToCamera(group);
if (!this.targetCenter) { if (!this.targetCenter) {
const box = new Box3(); const box = new Box3();
this.targetCenter = box.setFromObject(this.mesh).getCenter(); const center = new Vector3();
this.targetCenter = box.setFromObject(this.mesh).getCenter(center);
} }
this.updateControlsTarget(this.targetCenter); this.updateControlsTarget(this.targetCenter);
} }
@ -452,10 +437,7 @@ class ThreeViewer extends React.PureComponent<Props, State> {
gammaCorrection: true, gammaCorrection: true,
}); });
this.scene = ThreeScene({ this.scene = ThreeScene(this.theme);
showFog: true,
...this.theme,
});
const canvas = this.renderer.domElement; const canvas = this.renderer.domElement;
const { offsetWidth: width, offsetHeight: height } = this.viewer || {}; const { offsetWidth: width, offsetHeight: height } = this.viewer || {};
@ -473,7 +455,7 @@ class ThreeViewer extends React.PureComponent<Props, State> {
// Set viewer size // Set viewer size
this.renderer.setSize(width, height); this.renderer.setSize(width, height);
// Create model material // Create material
this.material = new MeshPhongMaterial({ this.material = new MeshPhongMaterial({
depthWrite: true, depthWrite: true,
flatShading: true, flatShading: true,
@ -501,26 +483,21 @@ class ThreeViewer extends React.PureComponent<Props, State> {
} }
render() { render() {
const { theme } = this.props;
const { error, isReady, isLoading } = this.state; const { error, isReady, isLoading } = this.state;
const loadingMessage = __('Loading 3D model.'); const loadingMessage = __('Loading 3D model.');
const showViewer = isReady && !error; const showLoading = isLoading && !error && !isReady;
const showLoading = isLoading && !error; const containerClass = 'gui-container';
// Adaptive theme for gui controls
const containerClass = classNames('gui-container', { light: theme === 'light' });
return ( return (
<React.Fragment> <>
<div className="file-render__viewer file-render__viewer--three">
<div ref={element => (this.guiContainer = element)} className={containerClass} />
<div className="three-viewer" ref={viewer => (this.viewer = viewer)}>
{error && <LoadingScreen status={error} spinner={false} />} {error && <LoadingScreen status={error} spinner={false} />}
{showLoading && <LoadingScreen status={loadingMessage} spinner />} {showLoading && <LoadingScreen status={loadingMessage} spinner />}
<div ref={element => (this.guiContainer = element)} className={containerClass} /> </div>
<div </div>
style={{ opacity: showViewer ? 1 : 0 }} </>
className="three-viewer file-render__viewer"
ref={viewer => (this.viewer = viewer)}
/>
</React.Fragment>
); );
} }
} }

View file

@ -21,17 +21,18 @@ const addLights = (scene, color, groundColor) => {
scene.add(shadowLight); scene.add(shadowLight);
}; };
const ViewerScene = ({ backgroundColor, groundColor, showFog }) => { const ViewerScene = ({ backgroundColor, showFog }) => {
// Convert color
const bgColor = new Color(backgroundColor);
// New scene // New scene
const bg = new Color(backgroundColor);
const scene = new Scene(); const scene = new Scene();
// Background color // Transparent background
scene.background = bgColor; scene.background = bg;
// Fog effect // Add fog
scene.fog = showFog === true ? new Fog(bgColor, 1, 95) : null; if (showFog) {
scene.fog = new Fog(bg, 1, 54);
}
// Add basic lights // Add basic lights
addLights(scene, '#FFFFFF', groundColor); addLights(scene, 0xffffff, bg);
// Return new three scene // Return new three scene
return scene; return scene;
}; };

View file

@ -16,7 +16,10 @@ export const IMAGE = 'image';
export const CAD = 'cad'; export const CAD = 'cad';
export const COMIC = 'comic'; export const COMIC = 'comic';
export const AUTO_RENDER_MODES = [IMAGE, CAD, COMIC].concat(TEXT_MODES); // these types will render (and thus download) automatically (if free) // These types can only be render if download completed
export const NON_STREAM_MODES = [CAD, COMIC];
export const AUTO_RENDER_MODES = [IMAGE].concat(TEXT_MODES);
export const WEB_SHAREABLE_MODES = AUTO_RENDER_MODES.concat(FLOATING_MODES); export const WEB_SHAREABLE_MODES = AUTO_RENDER_MODES.concat(FLOATING_MODES);
export const DOWNLOAD = 'download'; export const DOWNLOAD = 'download';
@ -25,7 +28,7 @@ export const UNSUPPORTED = 'unsupported';
// PDFs disabled on desktop until we update Electron: https://github.com/electron/electron/issues/12337 // PDFs disabled on desktop until we update Electron: https://github.com/electron/electron/issues/12337
// Comics disabled because nothing is actually reporting as a comic type // Comics disabled because nothing is actually reporting as a comic type
export const UNSUPPORTED_IN_THIS_APP = IS_WEB ? [CAD, COMIC, APPLICATION] : [CAD, APPLICATION, PDF]; export const UNSUPPORTED_IN_THIS_APP = IS_WEB ? [CAD, COMIC, APPLICATION] : [APPLICATION, PDF];
export const UNRENDERABLE_MODES = Array.from( export const UNRENDERABLE_MODES = Array.from(
new Set(UNSUPPORTED_IN_THIS_APP.concat([DOWNLOAD, APPLICATION, UNSUPPORTED])) new Set(UNSUPPORTED_IN_THIS_APP.concat([DOWNLOAD, APPLICATION, UNSUPPORTED]))

View file

@ -3,101 +3,26 @@
.gui-container { .gui-container {
top: 0; top: 0;
right: 0; right: 0;
position: absolute; position: absolute;
z-index: 2;
.dg.main { .dg.main {
margin: 0; margin: 0;
padding: 0; padding: 0;
overflow: inherit; overflow: inherit;
}
// Light theme:
// https://github.com/liabru/dat-gui-light-theme
&.light {
.dg.main.taller-than-window .close-button {
border-top: 1px solid #ddd;
}
.dg.main .close-button {
&:not(:hover) {
background-color: #e8e8e8;
}
&:hover {
background-color: #ddd;
}
}
.dg {
color: #555;
text-shadow: none !important;
&.main {
&::-webkit-scrollbar {
background-color: #fafafa;
}
&::-webkit-scrollbar-thumb {
background-color: #bbb;
}
}
li { li {
&:not(.folder) { margin: 0;
background-color: #fafafa;
border-bottom: 1px solid #ddd;
} }
&.save-row .button {
text-shadow: none !important;
}
&.title {
background: #e8e8e8
url('data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlI+hKgFxoCgAOw==')
6px 10px no-repeat;
}
}
.cr {
&.function:hover,
&.boolean:hover {
background-color: white;
}
}
.c {
input { input {
&[type='text'] { height: initial;
&:not(:focus),
&:not(:hover) {
background-color: #e9e9e9;
} }
&:focus, input[type='checkbox'] {
&:hover { height: 12px;
background-color: #eee; width: 12px;
} padding: 0;
margin: 0;
&:focus {
color: #555;
}
}
}
.slider {
&:not(:hover) {
background-color: #e9e9e9;
}
&:hover {
background-color: #eee;
}
}
}
} }
} }
} }

View file

@ -119,6 +119,15 @@
} }
} }
.file-render__viewer--three {
position: relative;
overflow: hidden;
.three-viewer {
height: calc(100vh - var(--header-height) - var(--spacing-medium) * 2);
}
}
.file-render__content { .file-render__content {
width: 100%; width: 100%;
height: 100%; height: 100%;