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:
parent
ae3c8d5221
commit
e4d822f818
6 changed files with 79 additions and 154 deletions
|
@ -2,6 +2,7 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import FileRender from 'component/fileRender';
|
||||
import LoadingScreen from 'component/common/loading-screen';
|
||||
import { NON_STREAM_MODES } from 'constants/file_render_modes';
|
||||
|
||||
type Props = {
|
||||
isPlaying: boolean,
|
||||
|
@ -14,9 +15,18 @@ type 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 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(() => {
|
||||
if (isPlaying) {
|
||||
|
@ -39,5 +49,5 @@ export default function FileRenderInline(props: Props) {
|
|||
return null;
|
||||
}
|
||||
|
||||
return isReadyToPlay ? <FileRender uri={uri} /> : <LoadingScreen status={__('Preparing your content')} />;
|
||||
return renderContent ? <FileRender uri={uri} /> : <LoadingScreen status={__('Preparing your content')} />;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// @flow
|
||||
import * as React from 'react';
|
||||
import * as dat from 'dat.gui';
|
||||
import classNames from 'classnames';
|
||||
import LoadingScreen from 'component/common/loading-screen';
|
||||
|
||||
// ThreeJS
|
||||
|
@ -52,14 +51,13 @@ const ThreeLoader = ({ fileType = null, downloadPath = null }, renderModel, mana
|
|||
};
|
||||
|
||||
type viewerTheme = {
|
||||
gridColor: string,
|
||||
groundColor: string,
|
||||
backgroundColor: string,
|
||||
centerLineColor: string,
|
||||
showFog: boolean,
|
||||
gridColor: number,
|
||||
backgroundColor: number,
|
||||
centerLineColor: number,
|
||||
};
|
||||
|
||||
type Props = {
|
||||
theme: string,
|
||||
source: {
|
||||
fileType: string,
|
||||
downloadPath: string,
|
||||
|
@ -131,26 +129,15 @@ class ThreeViewer extends React.PureComponent<Props, State> {
|
|||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
const { theme } = this.props;
|
||||
// Object defualt color
|
||||
this.materialColor = '#44b098';
|
||||
// Viewer themes
|
||||
this.themes = {
|
||||
dark: {
|
||||
gridColor: '#414e5c',
|
||||
groundColor: '#13233C',
|
||||
backgroundColor: '#13233C',
|
||||
centerLineColor: '#7f8c8d',
|
||||
},
|
||||
light: {
|
||||
gridColor: '#7f8c8d',
|
||||
groundColor: '#DDD',
|
||||
backgroundColor: '#EEE',
|
||||
centerLineColor: '#2F2F2F',
|
||||
},
|
||||
this.materialColor = 0xffffff;
|
||||
// Default viewer Theme
|
||||
this.theme = {
|
||||
showFog: true,
|
||||
gridColor: 0x414e5c,
|
||||
centerLineColor: 0x7f8c8d,
|
||||
backgroundColor: 0x0b0c0d,
|
||||
};
|
||||
// Select current theme
|
||||
this.theme = this.themes[theme] || this.themes.light;
|
||||
// State
|
||||
this.state = {
|
||||
error: null,
|
||||
|
@ -212,29 +199,27 @@ class ThreeViewer extends React.PureComponent<Props, State> {
|
|||
});
|
||||
// Clean up controls
|
||||
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) {
|
||||
this.renderer.context = null;
|
||||
this.renderer.domElement = null;
|
||||
this.renderer.renderLists.dispose();
|
||||
this.renderer.forceContextLoss();
|
||||
this.renderer.dispose();
|
||||
this.renderer = null;
|
||||
}
|
||||
// Stop animation
|
||||
cancelAnimationFrame(this.frameID);
|
||||
// Destroy GUI Controls
|
||||
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
|
||||
theme: viewerTheme;
|
||||
themes: { dark: viewerTheme, light: viewerTheme };
|
||||
materialColor: string;
|
||||
materialColor: number;
|
||||
// Refs
|
||||
viewer: ?HTMLElement;
|
||||
guiContainer: ?HTMLElement;
|
||||
|
@ -265,10 +250,10 @@ class ThreeViewer extends React.PureComponent<Props, State> {
|
|||
|
||||
transformGroup(group: any) {
|
||||
ThreeViewer.fitMeshToCamera(group);
|
||||
|
||||
if (!this.targetCenter) {
|
||||
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);
|
||||
}
|
||||
|
@ -452,10 +437,7 @@ class ThreeViewer extends React.PureComponent<Props, State> {
|
|||
gammaCorrection: true,
|
||||
});
|
||||
|
||||
this.scene = ThreeScene({
|
||||
showFog: true,
|
||||
...this.theme,
|
||||
});
|
||||
this.scene = ThreeScene(this.theme);
|
||||
|
||||
const canvas = this.renderer.domElement;
|
||||
const { offsetWidth: width, offsetHeight: height } = this.viewer || {};
|
||||
|
@ -473,7 +455,7 @@ class ThreeViewer extends React.PureComponent<Props, State> {
|
|||
// Set viewer size
|
||||
this.renderer.setSize(width, height);
|
||||
|
||||
// Create model material
|
||||
// Create material
|
||||
this.material = new MeshPhongMaterial({
|
||||
depthWrite: true,
|
||||
flatShading: true,
|
||||
|
@ -501,26 +483,21 @@ class ThreeViewer extends React.PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { theme } = this.props;
|
||||
const { error, isReady, isLoading } = this.state;
|
||||
const loadingMessage = __('Loading 3D model.');
|
||||
const showViewer = isReady && !error;
|
||||
const showLoading = isLoading && !error;
|
||||
|
||||
// Adaptive theme for gui controls
|
||||
const containerClass = classNames('gui-container', { light: theme === 'light' });
|
||||
const showLoading = isLoading && !error && !isReady;
|
||||
const containerClass = 'gui-container';
|
||||
|
||||
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} />}
|
||||
{showLoading && <LoadingScreen status={loadingMessage} spinner />}
|
||||
<div ref={element => (this.guiContainer = element)} className={containerClass} />
|
||||
<div
|
||||
style={{ opacity: showViewer ? 1 : 0 }}
|
||||
className="three-viewer file-render__viewer"
|
||||
ref={viewer => (this.viewer = viewer)}
|
||||
/>
|
||||
</React.Fragment>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,17 +21,18 @@ const addLights = (scene, color, groundColor) => {
|
|||
scene.add(shadowLight);
|
||||
};
|
||||
|
||||
const ViewerScene = ({ backgroundColor, groundColor, showFog }) => {
|
||||
// Convert color
|
||||
const bgColor = new Color(backgroundColor);
|
||||
const ViewerScene = ({ backgroundColor, showFog }) => {
|
||||
// New scene
|
||||
const bg = new Color(backgroundColor);
|
||||
const scene = new Scene();
|
||||
// Background color
|
||||
scene.background = bgColor;
|
||||
// Fog effect
|
||||
scene.fog = showFog === true ? new Fog(bgColor, 1, 95) : null;
|
||||
// Transparent background
|
||||
scene.background = bg;
|
||||
// Add fog
|
||||
if (showFog) {
|
||||
scene.fog = new Fog(bg, 1, 54);
|
||||
}
|
||||
// Add basic lights
|
||||
addLights(scene, '#FFFFFF', groundColor);
|
||||
addLights(scene, 0xffffff, bg);
|
||||
// Return new three scene
|
||||
return scene;
|
||||
};
|
||||
|
|
|
@ -16,7 +16,10 @@ export const IMAGE = 'image';
|
|||
export const CAD = 'cad';
|
||||
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 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
|
||||
// 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(
|
||||
new Set(UNSUPPORTED_IN_THIS_APP.concat([DOWNLOAD, APPLICATION, UNSUPPORTED]))
|
||||
|
|
|
@ -3,101 +3,26 @@
|
|||
.gui-container {
|
||||
top: 0;
|
||||
right: 0;
|
||||
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
|
||||
.dg.main {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
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 {
|
||||
&:not(.folder) {
|
||||
background-color: #fafafa;
|
||||
border-bottom: 1px solid #ddd;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&.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 {
|
||||
&[type='text'] {
|
||||
&:not(:focus),
|
||||
&:not(:hover) {
|
||||
background-color: #e9e9e9;
|
||||
height: initial;
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
color: #555;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.slider {
|
||||
&:not(:hover) {
|
||||
background-color: #e9e9e9;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #eee;
|
||||
}
|
||||
}
|
||||
}
|
||||
input[type='checkbox'] {
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
|
Loading…
Reference in a new issue