Skip to content

Commit

Permalink
feat: renderOrder and depTest
Browse files Browse the repository at this point in the history
fix: scroll
improve performance for scroll
  • Loading branch information
bbohlender committed Apr 11, 2024
1 parent a789caa commit 14d404e
Show file tree
Hide file tree
Showing 21 changed files with 313 additions and 164 deletions.
13 changes: 10 additions & 3 deletions examples/uikit/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,15 @@ export default function App() {
<ambientLight intensity={0.5} />
<directionalLight intensity={10} position={[5, 1, 10]} />
<Gltf position={[200, 0, 200]} scale={0.1} src="scene.glb" />
<Gltf position={[0, 0, 4]} scale={10} src="example.glb" />
<RenderTexture ref={(t) => (texture.value = t ?? undefined)}>
<Box />
</RenderTexture>
<Box renderOrder={1} position={[0, 0, 4]} scale={0.2}>
<meshBasicMaterial depthWrite={false} transparent color="red" />
</Box>
<Fullscreen
renderOrder={10}
distanceToCamera={1}
gap={10}
overflow="scroll"
Expand Down Expand Up @@ -117,7 +122,9 @@ export default function App() {
</mesh>
</Content>
<Content flexShrink={0} width={100}>
<Gltf src="example.glb" />
<Box>
<meshBasicMaterial transparent color="black" />
</Box>
</Content>
<Svg flexShrink={0} marginLeft={-100} color={x} backgroundColor="red" src="example.svg" width={200} />
<Suspense fallback={<Skeleton width={300} aspectRatio={2 / 3} />}>
Expand All @@ -127,8 +134,8 @@ export default function App() {
fit="cover"
border={20}
ref={ref}
onHoverChange={(hovered) => ref.current?.setStyle({ borderOpacity: hovered ? 10 : 0.2 })}
borderOpacity={0.2}
onHoverChange={(hovered) => ref.current?.setStyle({ borderOpacity: hovered ? 1 : 0.5 })}
borderOpacity={0.5}
borderRadius={10}
flexDirection="column"
src="https://picsum.photos/2000/3000"
Expand Down
4 changes: 2 additions & 2 deletions packages/react/src/fullscreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import { Root } from './root.js'
import { batch, signal } from '@preact/signals-core'
import { RootState, createPortal, useStore, useThree } from '@react-three/fiber'
import { EventHandlers } from '@react-three/fiber/dist/declarations/src/core/events.js'
import { RootProperties, updateSizeFullscreen } from '@pmndrs/uikit/internals'
import { RootProperties, WithReactive, updateSizeFullscreen } from '@pmndrs/uikit/internals'
import { ComponentInternals } from './ref.js'

export const Fullscreen: (
props: Omit<RootProperties, 'sizeX' | 'sizeY' | 'pixelSize'> & {
props: Omit<RootProperties & WithReactive<{ pixelSize?: number }>, 'sizeX' | 'sizeY' | 'pixelSize'> & {
children?: ReactNode
attachCamera?: boolean
distanceToCamera?: number
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/portal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export const Portal: (
vScene,
injectState,
)}
<Image {...props} src={fbo.texture} fit="fill" keepAspectRatio={false} ref={imageRef} />
<Image src={fbo.texture} fit="fill" keepAspectRatio={false} {...props} ref={imageRef} />
</>
)
},
Expand Down
14 changes: 11 additions & 3 deletions packages/react/src/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,40 @@ import { forwardRef, ReactNode, RefAttributes, useEffect, useMemo, useRef } from
import { ParentProvider } from './context.js'
import { AddHandlers, usePropertySignals } from './utilts.js'
import {
DEFAULT_PIXEL_SIZE,
RootProperties,
Subscriptions,
WithReactive,
createRoot,
initialize,
readReactive,
reversePainterSortStable,
unsubscribeSubscriptions,
} from '@pmndrs/uikit/internals'
import { Object3D } from 'three'
import { ComponentInternals, useComponentInternals } from './ref.js'
import { Signal, computed, signal } from '@preact/signals-core'

export const Root: (
props: RootProperties & {
children?: ReactNode
} & EventHandlers &
props: RootProperties &
WithReactive<{ pixelSize?: number }> & {
children?: ReactNode
} & EventHandlers &
RefAttributes<ComponentInternals<RootProperties>>,
) => ReactNode = forwardRef((properties, ref) => {
const renderer = useThree((state) => state.gl)
renderer.setTransparentSort(reversePainterSortStable)
const store = useStore()
const outerRef = useRef<Object3D>(null)
const innerRef = useRef<Object3D>(null)
const pixelSizeSignal = useMemo(() => signal<Signal<number | undefined> | number | undefined>(undefined), [])
pixelSizeSignal.value = properties.pixelSize
const propertySignals = usePropertySignals(properties)
const onFrameSet = useMemo(() => new Set<(delta: number) => void>(), [])
const internals = useMemo(
() =>
createRoot(
computed(() => readReactive(pixelSizeSignal.value) ?? DEFAULT_PIXEL_SIZE),
propertySignals.style,
propertySignals.properties,
propertySignals.default,
Expand Down
5 changes: 4 additions & 1 deletion packages/uikit/src/components/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { computedIsClipped, computedClippingRect } from '../clipping.js'
import {
ScrollbarProperties,
applyScrollPosition,
computedAnyAncestorScrollable,
computedGlobalScrollMatrix,
computedScrollHandlers,
createScrollPosition,
Expand Down Expand Up @@ -56,7 +57,7 @@ export function createContainer(
childrenContainer: Object3DRef,
) {
const node = signal<FlexNode | undefined>(undefined)
const flexState = createFlexNodeState(parentContext.anyAncestorScrollable)
const flexState = createFlexNodeState()
const hoveredSignal = signal<Array<number>>([])
const activeSignal = signal<Array<number>>([])
const initializers: Initializers = []
Expand Down Expand Up @@ -124,6 +125,7 @@ export function createContainer(
)
const scrollHandlers = computedScrollHandlers(
scrollPosition,
parentContext.anyAncestorScrollable,
flexState,
object,
properties,
Expand All @@ -136,6 +138,7 @@ export function createContainer(
setupViewportListeners(style, properties, isClipped, initializers)

return Object.assign(flexState, {
anyAncestorScrollable: computedAnyAncestorScrollable(flexState.scrollable, parentContext.anyAncestorScrollable),
clippingRect: computedClippingRect(
globalMatrix,
flexState,
Expand Down
120 changes: 69 additions & 51 deletions packages/uikit/src/components/content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import {
getDefaultPanelMaterialConfig,
makeClippedRaycast,
} from '../internals.js'
import { Box3, Mesh, Object3D, Vector3 } from 'three'
import { Box3, Material, Mesh, Object3D, Vector3 } from 'three'

export type InheritableContentProperties = WithClasses<
WithConditionals<
Expand Down Expand Up @@ -63,12 +63,12 @@ export function createContent(
properties: Signal<ContentProperties | undefined>,
defaultProperties: Signal<AllOptionalProperties | undefined>,
object: Object3DRef,
contentRef: Object3DRef,
contentContainerRef: Object3DRef,
) {
const hoveredSignal = signal<Array<number>>([])
const activeSignal = signal<Array<number>>([])
const initializers: Initializers = []
const flexState = createFlexNodeState(parentContext.anyAncestorScrollable)
const flexState = createFlexNodeState()

setupCursorCleanup(hoveredSignal, initializers)

Expand Down Expand Up @@ -139,7 +139,7 @@ export function createContent(
parentContext.clippingRect,
orderInfo,
sizeSignal,
contentRef,
contentContainerRef,
initializers,
),
interactionPanel: createInteractionPanel(
Expand Down Expand Up @@ -170,16 +170,27 @@ function createMeasureContent(
parentClippingRect: Signal<ClippingRect | undefined>,
orderInfo: Signal<OrderInfo | undefined>,
sizeSignal: Signal<Vector3>,
contentRef: Object3DRef,
contentContainerRef: Object3DRef,
initializers: Initializers,
) {
const clippingPlanes = createGlobalClippingPlanes(root, parentClippingRect, initializers)
const depthAlign = computedProperty(propertiesSignal, 'depthAlign', defaultDepthAlign)
const keepAspectRatio = computedProperty(propertiesSignal, 'keepAspectRatio', true)
const measuredSize = new Vector3()
const measuredCenter = new Vector3()
const updateRenderProperties = (content: Object3D | null, renderOrder: number, depthTest: boolean) =>
content?.traverse((object) => {
if (!(object instanceof Mesh)) {
return
}
object.renderOrder = renderOrder
if (!(object.material instanceof Material)) {
return
}
object.material.depthTest = depthTest
})
const measureContent = () => {
const content = contentRef.current
const content = contentContainerRef.current
if (content == null) {
measuredSize.copy(smallValue)
measuredCenter.set(0, 0, 0)
Expand All @@ -204,53 +215,60 @@ function createMeasureContent(
}
box3Helper.getCenter(measuredCenter)
}
initializers.push((subscriptions) => {
const content = contentRef.current
if (content == null) {
return subscriptions
}
measureContent()
subscriptions.push(
effect(() => {
const {
size: { value: size },
paddingInset: { value: paddingInset },
borderInset: { value: borderInset },
} = flexState
if (size == null || paddingInset == null || borderInset == null) {
return
}
const [width, height] = size
const [pTop, pRight, pBottom, pLeft] = paddingInset
const [bTop, bRight, bBottom, bLeft] = borderInset
const topInset = pTop + bTop
const rightInset = pRight + bRight
const bottomInset = pBottom + bBottom
const leftInset = pLeft + bLeft
initializers.push(
() =>
effect(() => updateRenderProperties(contentContainerRef.current, root.renderOrder.value, root.depthTest.value)),
(subscriptions) => {
const content = contentContainerRef.current
if (content == null) {
return subscriptions
}
measureContent()
subscriptions.push(
effect(() => {
const {
size: { value: size },
paddingInset: { value: paddingInset },
borderInset: { value: borderInset },
} = flexState
if (size == null || paddingInset == null || borderInset == null) {
return
}
const [width, height] = size
const [pTop, pRight, pBottom, pLeft] = paddingInset
const [bTop, bRight, bBottom, bLeft] = borderInset
const topInset = pTop + bTop
const rightInset = pRight + bRight
const bottomInset = pBottom + bBottom
const leftInset = pLeft + bLeft

const innerWidth = width - leftInset - rightInset
const innerHeight = height - topInset - bottomInset
const innerWidth = width - leftInset - rightInset
const innerHeight = height - topInset - bottomInset

const pixelSize = root.pixelSize.value
content.scale
.set(
innerWidth * pixelSize,
innerHeight * pixelSize,
keepAspectRatio.value ? (innerHeight * pixelSize * measuredSize.z) / measuredSize.y : measuredSize.z,
)
.divide(measuredSize)
const pixelSize = root.pixelSize.value
content.scale
.set(
innerWidth * pixelSize,
innerHeight * pixelSize,
keepAspectRatio.value ? (innerHeight * pixelSize * measuredSize.z) / measuredSize.y : measuredSize.z,
)
.divide(measuredSize)

content.position.copy(measuredCenter).negate()
content.position.copy(measuredCenter).negate()

content.position.z -= alignmentZMap[depthAlign.value] * measuredSize.z
content.position.multiply(content.scale)
content.position.add(
vectorHelper.set((leftInset - rightInset) * 0.5 * pixelSize, (bottomInset - topInset) * 0.5 * pixelSize, 0),
)
content.updateMatrix()
}),
)
return subscriptions
})
return measureContent
content.position.z -= alignmentZMap[depthAlign.value] * measuredSize.z
content.position.multiply(content.scale)
content.position.add(
vectorHelper.set((leftInset - rightInset) * 0.5 * pixelSize, (bottomInset - topInset) * 0.5 * pixelSize, 0),
)
content.updateMatrix()
}),
)
return subscriptions
},
)
return () => {
updateRenderProperties(contentContainerRef.current, root.renderOrder.peek(), root.depthTest.peek())
measureContent()
}
}
11 changes: 7 additions & 4 deletions packages/uikit/src/components/custom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export function createCustomContainer(
})

//create node
const flexState = createFlexNodeState(parentContext.anyAncestorScrollable)
const flexState = createFlexNodeState()
createNode(undefined, flexState, parentContext, mergedProperties, object, initializers)

//transform
Expand All @@ -90,9 +90,11 @@ export function createCustomContainer(
}
mesh.matrixAutoUpdate = false
if (mesh.material instanceof Material) {
mesh.material.clippingPlanes = clippingPlanes
mesh.material.needsUpdate = true
mesh.material.shadowSide = FrontSide
const material = mesh.material
material.clippingPlanes = clippingPlanes
material.needsUpdate = true
material.shadowSide = FrontSide
subscriptions.push(() => effect(() => (material.depthTest = parentContext.root.depthTest.value)))
}
mesh.raycast = makeClippedRaycast(
mesh,
Expand All @@ -103,6 +105,7 @@ export function createCustomContainer(
)
setupRenderOrder(mesh, parentContext.root, orderInfo)
subscriptions.push(
effect(() => (mesh.renderOrder = parentContext.root.renderOrder.value)),
effect(() => (mesh.receiveShadow = mergedProperties.value.read('receiveShadow', false))),
effect(() => (mesh.castShadow = mergedProperties.value.read('castShadow', false))),
effect(() => {
Expand Down
4 changes: 2 additions & 2 deletions packages/uikit/src/components/icon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export function createIcon(
},
)

const flexState = createFlexNodeState(parentContext.anyAncestorScrollable)
const flexState = createFlexNodeState()
createNode(undefined, flexState, parentContext, mergedProperties, object, initializers)

const transformMatrix = computedTransformMatrix(mergedProperties, flexState, parentContext.root.pixelSize)
Expand Down Expand Up @@ -219,6 +219,6 @@ function createIconGroup(
}),
() => effect(() => void (group.visible = !isClipped.value)),
)
applyAppearancePropertiesToGroup(propertiesSignal, group, initializers)
applyAppearancePropertiesToGroup(propertiesSignal, group, initializers, parentContext.root)
return group
}
Loading

0 comments on commit 14d404e

Please sign in to comment.