Skip to content

Commit

Permalink
fix shader node
Browse files Browse the repository at this point in the history
  • Loading branch information
emilwidlund committed Sep 25, 2024
1 parent 150c0b7 commit c1af13b
Show file tree
Hide file tree
Showing 9 changed files with 393 additions and 53 deletions.
1 change: 1 addition & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-draggable": "^4.4.6",
"react-focus-lock": "^2.13.2",
"react-hook-form": "^7.51.4",
"react-hotkeys-hook": "^4.4.4",
"rxjs": "^7.8.1",
Expand Down
101 changes: 76 additions & 25 deletions apps/web/src/circuit/components/Node/Node.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import { Circuit } from '@bitspace/circuit';
import { useRouter } from 'next/navigation';
import { get } from 'mobx';
import { Spinner } from '@/components/Spinner/Spinner';
import { NodeType, Shader } from '@bitspace/nodes';
import { useModal } from '@/hooks/useModal';
import { Modal } from '@/components/Modal/Modal';

export const Node = observer(
({ node, actions, window, onMoveStop }: NodeProps) => {
Expand All @@ -23,6 +26,8 @@ export const Node = observer(
const { store } = React.useContext(StoreContext);
const router = useRouter();

const { isShown, show, hide } = useModal();

const [windowActive, setWindowActive] = React.useState(false);

const nodeLoading = Object.values(node.outputs).some(output =>
Expand Down Expand Up @@ -177,8 +182,23 @@ export const Node = observer(
{/** @ts-ignore */}
<span>{node.constructor.displayName}</span>
<div className={nodeActionsClassNames}>
{node instanceof Shader && (
<>
<NodeAction
color="bg-yellow-400"
onClick={() => show()}
/>
<Modal
hide={hide}
isShown={isShown}
modalContent={
<FragmentEditor node={node} />
}
/>
</>
)}
<NodeAction
color="#ff4444"
color="bg-red-400"
onClick={handleRemoveNode}
/>
</div>
Expand All @@ -204,35 +224,35 @@ export const Node = observer(
const NodeAction = ({ color = '#fff', onClick }: NodeActionProps) => {
return (
<div
className="opacity-100 transition-opacity ml-1.5 w-2.5 h-2.5 rounded-md bg-red-400 hover:opactiy-40"
color={color}
className={clsx(
'opacity-100 transition-opacity w-2.5 h-2.5 rounded-md hover:opactiy-40',
color
)}
onClick={onClick}
/>
);
};

const NodePorts = ({
ports,
isOutputWrapper,
windowActive
}: NodePortsProps) => {
const nodePortsWrapperClassNames = clsx(
'flex flex-col flex-grow px-4 pb-5',
isOutputWrapper ? 'items-end' : 'items-start'
);
return (
<div className={nodePortsWrapperClassNames}>
{ports.map(port => (
<Port
key={port.id}
port={port}
isOutput={!!isOutputWrapper}
windowActive={windowActive}
/>
))}
</div>
);
};
const NodePorts = observer(
({ ports, isOutputWrapper, windowActive }: NodePortsProps) => {
const nodePortsWrapperClassNames = clsx(
'flex flex-col flex-grow px-4 pb-5',
isOutputWrapper ? 'items-end' : 'items-start'
);
return (
<div className={nodePortsWrapperClassNames}>
{ports.map(port => (
<Port
key={port.id}
port={port}
isOutput={!!isOutputWrapper}
windowActive={windowActive}
/>
))}
</div>
);
}
);

export const NodeWindow = ({
children,
Expand All @@ -250,3 +270,34 @@ export const NodeWindow = ({
</div>
);
};

const FragmentEditor = ({ node }: { node: Shader }) => {
const [fragment, setFragment] = React.useState('');

React.useEffect(() => {
const subscription = node.$fragmentShader.subscribe(setFragment);

return () => {
subscription.unsubscribe();
};
}, [node]);

return (
<div className="flex flex-col p-12 min-h-96">
<textarea
className="bg-slate-100 p-4 h-full rounded-2xl font-mono text-sm"
value={fragment}
onKeyDown={e => {
e.stopPropagation();
}}
onChange={e => {
e.stopPropagation();
setFragment(e.target.value);
}}
onBlur={e => {
node.$fragmentShader.next(fragment);
}}
/>
</div>
);
};
87 changes: 87 additions & 0 deletions apps/web/src/components/Modal/Modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import clsx from 'clsx';
import { motion } from 'framer-motion';
import React, {
FunctionComponent,
MouseEvent,
useCallback,
useEffect
} from 'react';
import ReactDOM from 'react-dom';
import FocusLock from 'react-focus-lock';

export interface ModalProps {
isShown: boolean;
hide: () => void;
modalContent: JSX.Element;
className?: string;
}

export const Modal: FunctionComponent<ModalProps> = ({
isShown,
hide,
modalContent,
className
}) => {
const ref = React.useRef<HTMLDivElement>(null);

const onKeyDown = useCallback(
(event: React.KeyboardEvent) => {
const contains = ref.current?.contains(event.target as Node);

if (event.keyCode === 27 && isShown && contains) {
hide();
}
},
[hide, isShown]
);

useEffect(() => {
isShown
? (document.body.style.overflow = 'hidden')
: (document.body.style.overflow = 'unset');
}, [isShown, hide]);

const onInnerClick = (e: MouseEvent) => {
e.stopPropagation();
};

const modal = (
<React.Fragment>
<FocusLock>
<div
ref={ref}
className="fixed bottom-0 left-0 right-0 top-0 z-50 overflow-hidden focus-within:outline-none"
aria-modal
tabIndex={-1}
role="dialog"
onKeyDown={onKeyDown}
>
<div
className="flex h-full flex-col items-center bg-black/50 p-2 md:w-full"
onClick={e => {
e.preventDefault();
e.stopPropagation();
hide();
}}
>
<div className="block h-[80px] w-2 lg:max-h-[10%] lg:grow-[2]"></div>
<motion.div
className={clsx(
'rounded-3xl relative z-10 flex max-h-full w-full flex-col overflow-hidden bg-white shadow lg:w-[800px] lg:max-w-full dark:border',
className
)}
initial={{ opacity: 0, scale: 0.99 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.1, ease: 'easeInOut' }}
onClick={onInnerClick}
>
{modalContent}
</motion.div>
</div>
</div>
</FocusLock>
</React.Fragment>
);

return isShown ? ReactDOM.createPortal(modal, document.body) : null;
};
10 changes: 2 additions & 8 deletions apps/web/src/containers/PropertyPanel/PropertyPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,8 @@ export const PropertyPanel = observer(
const { store } = useContext(StoreContext);
const selectedNode = store.selectedNodes[0];

const inputs = useMemo(
() => Object.values(selectedNode?.inputs ?? {}),
[selectedNode]
);
const outputs = useMemo(
() => Object.values(selectedNode?.outputs ?? {}),
[selectedNode]
);
const inputs = Object.values(selectedNode?.inputs ?? {});
const outputs = Object.values(selectedNode?.outputs ?? {});

if (!selectedNode) {
return null;
Expand Down
14 changes: 14 additions & 0 deletions apps/web/src/hooks/useModal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { useState } from 'react'

export const useModal = (initialShown: boolean = false) => {
const [isShown, setIsShown] = useState<boolean>(initialShown)
const toggle = () => setIsShown(!isShown)
const show = () => setIsShown(true)
const hide = () => setIsShown(false)
return {
isShown,
toggle,
show,
hide,
}
}
28 changes: 17 additions & 11 deletions apps/web/src/windows/ShaderWindow.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
import { observer } from 'mobx-react-lite';
import { NodeWindow } from '@/circuit/components/Node/Node';
import { useEffect, useRef, useState } from 'react';
import { forwardRef, useEffect, useRef, useState } from 'react';
import { Shader } from '@bitspace/nodes';
import { Canvas, useThree } from '@react-three/fiber';
import { ShaderMaterial } from 'three';
import { Canvas, useFrame, useThree } from '@react-three/fiber';
import { BufferGeometry, ShaderMaterial } from 'three';

const Scene = ({ material }: { material: ShaderMaterial }) => {
const viewport = useThree(state => state.viewport);
const Scene = forwardRef<THREE.Mesh, { material: ShaderMaterial }>(
({ material }, ref) => {
const viewport = useThree(state => state.viewport);

return (
<mesh scale={[viewport.width, viewport.height, 1]} material={material}>
<planeGeometry />
</mesh>
);
};
return (
<mesh
ref={ref}
scale={[viewport.width, viewport.height, 1]}
material={material}
>
<planeGeometry />
</mesh>
);
}
);

export const ShaderWindow = observer(({ node }: { node: Shader }) => {
const [material, setMaterial] = useState<ShaderMaterial | undefined>();
Expand Down
3 changes: 3 additions & 0 deletions packages/nodes/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,8 @@
"three": "^0.161.0",
"zod": "^3.23.8"
},
"peerDependencies": {
"mobx": "^6.8.0"
},
"type": "module"
}
Loading

0 comments on commit c1af13b

Please sign in to comment.