Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

loading and error enhancements in studio #9225

Merged
merged 9 commits into from
Nov 13, 2023
2 changes: 1 addition & 1 deletion packages/client-core/i18n/en/editor.json
Original file line number Diff line number Diff line change
Expand Up @@ -911,7 +911,7 @@
"lbl-collapseAll": "Collapse All",
"lbl-explode": "Explode Objects",
"lbl-addEntity": "Add Entity",
"isseus": "Issues:"
"issues": "Issues"
},
"materialLibrary": {
"lbl": "Material Library",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'
import ArrowRightIcon from '@mui/icons-material/ArrowRight'

import { ErrorComponent } from '@etherealengine/engine/src/scene/components/ErrorComponent'
import { SceneAssetPendingTagComponent } from '@etherealengine/engine/src/scene/components/SceneAssetPendingTagComponent'
import CircularProgress from '@etherealengine/ui/src/primitives/mui/CircularProgress'
import { ItemTypes, SupportedFileTypes } from '../../constants/AssetTypes'
import { EntityNodeEditor } from '../../functions/ComponentEditors'
import { EditorControlFunctions } from '../../functions/EditorControlFunctions'
Expand Down Expand Up @@ -96,7 +98,8 @@ export const HierarchyTreeNode = (props: HierarchyTreeNodeProps) => {
const nodeName = useComponent(node.entity, NameComponent).value

const errors = node.entity ? useOptionalComponent(node.entity as Entity, ErrorComponent) : undefined
const firstError = errors?.keys[0]

const sceneAssetLoading = useOptionalComponent(node.entity as Entity, SceneAssetPendingTagComponent)

const onClickToggle = useCallback(
(e: MouseEvent) => {
Expand Down Expand Up @@ -330,7 +333,8 @@ export const HierarchyTreeNode = (props: HierarchyTreeNodeProps) => {
</div>
)}
</div>
{firstError && <NodeIssuesIcon node={[{ severity: 'error', message: firstError }]} />}
{errors?.value && <NodeIssuesIcon errors={errors.value} />}
{sceneAssetLoading?.value && <CircularProgress className={styles.assetLoadingIndicator} />}
</div>
</div>

Expand Down
80 changes: 24 additions & 56 deletions packages/editor/src/components/hierarchy/NodeIssuesIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,81 +23,49 @@ All portions of the code written by the Ethereal Engine team are Copyright © 20
Ethereal Engine. All Rights Reserved.
*/

import React, { useCallback, useMemo } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'

import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline'

import { ErrorComponentType } from '@etherealengine/engine/src/scene/components/ErrorComponent'
import Tooltip from '../layout/Tooltip'
import styles from './styles.module.scss'

const issuesTooltipContainerStyles = {
display: 'inline-block',
pointerEvents: 'none',
backgroundColor: 'rgba(21, 23, 27, 0.9)',
borderRadius: '3px',
padding: '8px',
maxWidth: '320px',
overflow: 'hidden',
overflowWrap: 'break-word',
userSelect: 'none'
}

const issueIconStyles = (color): React.CSSProperties => ({
const issueIconStyles = {
width: '16px',
height: 'auto',
fontSize: 'inherit',
color: color
})
color: '#ff0000'
}

export function NodeIssuesIcon({ node }) {
const theme = useMemo(
() => ({
yellow: '#ffcc00',
red: '#ff0000'
// Add other theme colors here
}),
[]
)
export function NodeIssuesIcon({ errors }: { errors: ErrorComponentType }) {
const { t } = useTranslation()

const severityToColor = useMemo(
() => ({
warning: theme.yellow,
error: theme.red
}),
[theme]
)
const renderTooltipInfo = () => {
let errorDetails: string[] = []

Object.entries(errors).forEach(([componentName, errorDetail]) => {
Object.entries(errorDetail).forEach(([errorKey, errorValue]) => {
errorDetails.push(
errorValue ? `${componentName} (${errorKey}): ${errorValue}` : `${componentName}: ${errorKey}`
)
})
})

const renderInfo = useCallback(() => {
return (
<div style={issuesTooltipContainerStyles as React.CSSProperties}>
<h6 style={{ fontSize: '100%', fontWeight: 'normal' }}>{t('editor:hierarchy.isseus')}</h6>
<ul style={{ listStyle: 'none' }}>
{node.map((issue, i) => {
return (
<li key={i}>
<ErrorOutlineIcon style={issueIconStyles(severityToColor[issue.severity])} fontSize="small" />{' '}
{issue.message}
</li>
)
})}
</ul>
<div className={styles.issuesTooltipContainer}>
<span className={styles.issuesTooltipHeading}>{t('editor:hierarchy.issues')}</span>
{errorDetails.map((errorDetail) => (
<div style={{ marginTop: '0.5rem' }}>{errorDetail}</div>
))}
</div>
)
}, [node, severityToColor])

let maxSeverity = 'warning'

for (const issue of node) {
if (issue.severity === 'error') {
maxSeverity = 'error'
break
}
}

return (
<Tooltip title={renderInfo()}>
<ErrorOutlineIcon style={issueIconStyles(severityToColor[maxSeverity])} />
<Tooltip title={renderTooltipInfo()}>
<ErrorOutlineIcon style={issueIconStyles} />
</Tooltip>
)
}
Expand Down
32 changes: 29 additions & 3 deletions packages/editor/src/components/hierarchy/styles.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
padding: 2px 4px;
outline: none;
border-radius: 3px;
margin:0;
margin: 0;
}
}

Expand Down Expand Up @@ -125,8 +125,8 @@
border: none;
background: transparent;
color: var(--textColor);
margin:0;
margin: 0;

&:hover {
opacity: 0.8;
color: var(--textColor);
Expand Down Expand Up @@ -162,3 +162,29 @@
.checkboxChecked {
color: var(--iconButtonColor) !important;
}

.assetLoadingIndicator {
width: 1rem !important;
height: 1rem !important;
color: var(--textColor);
}

.issuesTooltipContainer {
display: inline-block;
pointer-events: none;
background-color: rgb(21 23 27 / 90%);
border-radius: 3px;
padding: 8px;
max-width: 320px;
overflow: hidden;
overflow-wrap: break-word;
user-select: none;

.issuesTooltipHeading {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 1rem;
font-weight: bolder;
}
}
4 changes: 3 additions & 1 deletion packages/engine/src/scene/components/ColliderComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ export const ColliderComponent = defineComponent({
const groupComponent = useOptionalComponent(entity, GroupComponent)

useEffect(() => {
setComponent(entity, SceneAssetPendingTagComponent)

const isMeshCollider = [ShapeType.TriMesh, ShapeType.ConvexPolyhedron].includes(colliderComponent.shapeType.value)
const physicsWorld = getState(PhysicsState).physicsWorld

Expand Down Expand Up @@ -265,7 +267,7 @@ export const ColliderComponent = defineComponent({
rigidbody.scale.copy(transformComponent.scale.value)
}

if (hasComponent(entity, SceneAssetPendingTagComponent)) removeComponent(entity, SceneAssetPendingTagComponent)
removeComponent(entity, SceneAssetPendingTagComponent)
}, [isLoadedFromGLTF, colliderComponent, transformComponent, localTransformComponent, groupComponent?.length])

return null
Expand Down
4 changes: 3 additions & 1 deletion packages/engine/src/scene/components/GroundPlaneComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ export const GroundPlaneComponent = defineComponent({
const component = useComponent(entity, GroundPlaneComponent)

useEffect(() => {
setComponent(entity, SceneAssetPendingTagComponent)

const radius = 10000

const mesh = new Mesh(
Expand All @@ -118,7 +120,7 @@ export const GroundPlaneComponent = defineComponent({
const physicsWorld = getState(PhysicsState).physicsWorld
Physics.createRigidBody(entity, physicsWorld, rigidBodyDesc, [colliderDesc])

if (hasComponent(entity, SceneAssetPendingTagComponent)) removeComponent(entity, SceneAssetPendingTagComponent)
removeComponent(entity, SceneAssetPendingTagComponent)

return () => {
if (!entityExists(entity)) return
Expand Down
7 changes: 6 additions & 1 deletion packages/engine/src/scene/components/ImageComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,14 @@ import { useHookstate } from '@etherealengine/hyperflux'
import config from '@etherealengine/common/src/config'
import { AssetLoader } from '../../assets/classes/AssetLoader'
import { AssetClass } from '../../assets/enum/AssetClass'
import { defineComponent, useComponent } from '../../ecs/functions/ComponentFunctions'
import { defineComponent, removeComponent, setComponent, useComponent } from '../../ecs/functions/ComponentFunctions'
import { useEntityContext } from '../../ecs/functions/EntityFunctions'
import { EngineRenderer } from '../../renderer/WebGLRendererSystem'
import { StaticResourceType } from '../../schemas/media/static-resource.schema'
import { ImageAlphaMode, ImageAlphaModeType, ImageProjection, ImageProjectionType } from '../classes/ImageUtils'
import { addObjectToGroup, removeObjectFromGroup } from '../components/GroupComponent'
import { addError, clearErrors } from '../functions/ErrorFunctions'
import { SceneAssetPendingTagComponent } from './SceneAssetPendingTagComponent'

export const PLANE_GEO = new PlaneGeometry(1, 1, 1, 1)
export const SPHERE_GEO = new SphereGeometry(1, 64, 32)
Expand Down Expand Up @@ -163,13 +164,17 @@ export function ImageReactor() {
return addError(entity, ImageComponent, `UNSUPPORTED_ASSET_CLASS`)
}

setComponent(entity, SceneAssetPendingTagComponent)
AssetLoader.loadAsync(image.source.value)
.then((_texture) => {
texture.set(_texture)
})
.catch((e) => {
addError(entity, ImageComponent, `LOADING_ERROR`, e.message)
})
.finally(() => {
removeComponent(entity, SceneAssetPendingTagComponent)
})

return () => {
// TODO: abort load request, pending https://github.com/mrdoob/three.js/pull/23070
Expand Down
2 changes: 2 additions & 0 deletions packages/engine/src/scene/components/ModelComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ function ModelReactor() {
case 'fbx':
case 'vrm':
case 'usdz':
setComponent(entity, SceneAssetPendingTagComponent)
AssetLoader.load(
model.src,
{
Expand All @@ -176,6 +177,7 @@ function ModelReactor() {
if (fileExtension == 'vrm') (model.asset as any).userData = { flipped: true }
model.scene && removeObjectFromGroup(entity, model.scene)
modelComponent.scene.set(loadedAsset.scene)
removeComponent(entity, SceneAssetPendingTagComponent)
},
(onprogress) => {
if (!hasComponent(entity, SceneAssetPendingTagComponent)) return
Expand Down