Merge pull request #44 from storytold/feature/scene-object_control-panel

Feature/scene object control panel
This commit is contained in:
ArEnSc authored and GitHub committed 2024-03-29 19:28:33 -04:00
commit 2cf92154ba
13 files changed
+307 -31

No files matched your search

+2 -2
View File
@@ -33,14 +33,14 @@ export const Button = ({
}
const className = twMerge(
"text-sm font-semibold rounded-md px-3 py-2 shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 transition-all duration-150",
"text-sm font-semibold rounded-md px-3 py-2 shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 transition-all duration-150 flex gap-2 items-center",
getVariantClassNames(variant),
propsClassName,
);
return (
<button className={className} disabled={disabled} {...rest}>
{icon && <FontAwesomeIcon className="mr-2" icon={icon} />}
{icon && <FontAwesomeIcon icon={icon} />}
{children}
</button>
);
+1 -7
View File
@@ -13,13 +13,7 @@ export function Input({ label, className, ...rest }: InputProps) {
)}
<input
className="h-10 rounded-md bg-brand-secondary p-3 text-white"
style={{
outline: "2px solid transparent",
transition: "outline-color 0.15s ease-in-out",
}}
onFocus={(e) => (e.currentTarget.style.outlineColor = "#e66462")}
onBlur={(e) => (e.currentTarget.style.outlineColor = "transparent")}
className="h-10 rounded-md bg-brand-secondary p-3 text-white transition-all duration-150 ease-in-out outline-none outline-offset-0 focus:outline-brand-primary"
{...rest}
/>
</div>
+65
View File
@@ -0,0 +1,65 @@
import { useRef } from "react";
import { twMerge } from "tailwind-merge";
interface InputVectorProps {
x: number;
y: number;
z: number;
onChange : (v:{
x:number;
y:number;
z:number;
})=>void;
}
export const InputVector = ({
x,y,z,
onChange
}:InputVectorProps)=>{
const xRef = useRef<HTMLInputElement>(null);
const yRef = useRef<HTMLInputElement>(null);
const zRef = useRef<HTMLInputElement>(null);
const inputCommonClasses ="relative w-14 h-8 rounded-r-md bg-brand-secondary p-3 text-base text-white transition-all duration-150 ease-in-out outline-none -outline-offset-2 ";
const wrapperCommonClasses = "flex items-center before:inline-block before:w-10 before:h-10 before:bg-brand-primary before:text-white before:rounded-l-md before:h-8 before:w-4 before:text-center before:align-middle before:leading-8 text-xs";
function handleOnChange(){
const newVector = {
x: Number(xRef.current?.value),
y: Number(yRef.current?.value),
z: Number(zRef.current?.value),
}
onChange(newVector);
}
return(
<div className="w-full flex justify-between gap-2">
<span className={twMerge(wrapperCommonClasses, "before:bg-axis-x before:content-['X']")}>
<input
className={twMerge(inputCommonClasses, "focus:outline-axis-x")}
onChange={handleOnChange}
ref={xRef}
value={x}
/>
</span>
<span className={twMerge(wrapperCommonClasses, "before:bg-axis-y before:content-['Y']")}>
<input
className={twMerge(inputCommonClasses, "focus:outline-axis-y")}
onChange={handleOnChange}
ref={yRef}
value={y}
/>
</span>
<span className={twMerge(wrapperCommonClasses, "before:bg-axis-z before:content-['Z']")}>
<input
className={twMerge(inputCommonClasses, "focus:outline-axis-z")}
onChange={handleOnChange}
ref={zRef}
value={z}
/>
</span>
</div>
);
}
+2
View File
@@ -3,6 +3,7 @@ import { ButtonIcon } from "./ButtonIcon";
import { ButtonLink } from "./ButtonLink";
import { ButtonIconSelect } from "./ButtonIconSelect";
import { Input } from "./Input";
import { InputVector } from "./InputVector";
import { H1, H2, H3, H4, H5, H6, P, Label} from "./Typography";
import { ItemPicker } from "./ItemPicker";
import { Textarea } from "./Textarea";
@@ -18,6 +19,7 @@ export {
ItemPicker,
Textarea,
Input,
InputVector,
LoadingBar,
LoadingDots,
LoadingDotsBricks,
+7 -3
View File
@@ -5,12 +5,16 @@ import { faSparkles } from "@fortawesome/pro-solid-svg-icons";
import { Button, LoadingBar, LoadingDots } from "~/components";
import { TopBarHelmet } from "~/modules/TopBarHelmet/TopBarHelmet";
import { SidePanel } from "~/modules/SidePanel";
import { Controls3D } from "./comps/Controls3D";
import { ControlsTopButtons } from "./comps/ControlsTopButtons";
import { ControlsVideo } from "./comps/ControlsVideo";
import { ControlPanelSceneObject } from "./comps/ControlPanelSceneObject";
import { PreviewEngineCamera } from "./comps/PreviewEngineCamera";
import { Timeline } from "./comps/Timeline";
import { ViewSideBySide } from "./comps/ViewSideBySide";
import { SidePanelTabs } from "./comps/SidePanelTabs";
import { Timeline } from "./comps/Timeline";
import { EngineProvider } from "./contexts/EngineProvider";
import { AppUIProvider } from "./contexts/AppUiContext";
@@ -19,7 +23,6 @@ import {
appUiInitialStateValues,
APPUI_VIEW_MODES,
} from "./reducers";
import { ViewSideBySide } from "./comps/ViewSideBySide";
import {
canDrop,
clipLength,
@@ -52,7 +55,7 @@ export const PageEnigmaComponent = () => {
<div style={{ height: "calc(100vh - 68px)" }}>
{/* Engine section/side panel */}
<div
id="CanvasUiWrapper"
id="engine-n-panels-wrapper"
className="flex"
style={{ height: `calc(100% - ${lowerHeight}px)` }}
>
@@ -78,6 +81,7 @@ export const PageEnigmaComponent = () => {
<div className="absolute bottom-0 left-0 w-full">
<PreviewEngineCamera />
<ControlsVideo />
<ControlPanelSceneObject/>
</div>
</div>
{appUiState.viewMode === APPUI_VIEW_MODES.SIDE_BY_SIDE && (
@@ -0,0 +1,117 @@
import { useContext } from 'react';
import { Transition } from "@headlessui/react";
import { faArrowRightArrowLeft, faCube, faTrash} from "@fortawesome/pro-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { AppUiContext } from '../../contexts/AppUiContext';
import { Button, H4, H6, InputVector } from "~/components";
import { XYZ } from '../../datastructures/common';
import { ACTION_TYPES } from '../../reducers/appUiReducer/types';
export const ControlPanelSceneObject = () => {
const [appUiState, dispatchAppUiState ] = useContext(AppUiContext);
const position = appUiState?.currentSceneObject.objectVectors.position;
const rotation = appUiState?.currentSceneObject.objectVectors.rotation;
const scalar = appUiState?.currentSceneObject.objectVectors.scalar;
const handlePositionChange = (xyz:XYZ)=>{
if(appUiState)
dispatchAppUiState({
type: ACTION_TYPES.UPDATE_CONTROLPANELS_SCENEOBJECT,
payload:{
currentSceneObject:{
objectVectors:{
position: {...xyz},
rotation: appUiState.currentSceneObject.objectVectors.rotation,
scalar:appUiState.currentSceneObject.objectVectors.scalar,
}
}
}
});
}
const handleRotationChange = (xyz:XYZ)=>{
if(appUiState)
dispatchAppUiState({
type: ACTION_TYPES.UPDATE_CONTROLPANELS_SCENEOBJECT,
payload:{
currentSceneObject:{
objectVectors:{
position: appUiState.currentSceneObject.objectVectors.position,
rotation: {...xyz},
scalar:appUiState.currentSceneObject.objectVectors.scalar,
}
}
}
});
}
const handleScalarChange = (xyz:XYZ)=>{
if(appUiState)
dispatchAppUiState({
type: ACTION_TYPES.UPDATE_CONTROLPANELS_SCENEOBJECT,
payload:{
currentSceneObject:{
objectVectors:{
position: appUiState.currentSceneObject.objectVectors.position,
rotation: appUiState.currentSceneObject.objectVectors.rotation,
scalar:{...xyz},
}
}
}
});
}
return(
<Transition
show={appUiState?.currentSceneObject.isShowing}
className="absolute bottom-0 right-0 w-fit h-fit m-4 p-2 bg-ui-panel border border-ui-panel-border text-white rounded-lg flex flex-col gap-2"
enter="transition-opacity duration-150"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="transition-opacity duration-1000"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="flex justify-between gap-6">
<div className="flex items-center gap-2">
<FontAwesomeIcon icon={faCube}/>
<p>Object Name</p>
</div>
<div className="flex items-center gap-2">
<FontAwesomeIcon icon={faArrowRightArrowLeft} size="xs" />
<H6>Swap Object</H6>
</div>
</div>
<span className='h-1'/>
<H4>Position</H4>
<InputVector
x={position?.x || 0}
y={position?.y || 0}
z={position?.z || 0}
onChange={handlePositionChange}
/>
<H4>Rotation</H4>
<InputVector
x={rotation?.x || 0}
y={rotation?.y || 0}
z={rotation?.z || 0}
onChange={handleRotationChange}
/>
<H4>Scale</H4>
<InputVector
x={scalar?.x || 0}
y={scalar?.y || 0}
z={scalar?.z || 0}
onChange={handleScalarChange}
/>
<span className='h-2'/>
<div className="flex justify-between gap-2">
<Button variant="secondary">Add Keyframe (K)</Button>
<Button variant="secondary" icon={faTrash} />
</div>
</Transition>
);
}
@@ -0,0 +1 @@
export { ControlPanelSceneObject } from './ControlPanelSceneObject';
@@ -21,15 +21,6 @@ export const ControlsTopButtons = () => {
editorEngine?.loadScene(sceneToken);
};
const handleButtonCameraView = () => {
editorEngine?.switchCameraView();
};
const handleButtonPlayBack = () => {
editorEngine?.startPlayback();
};
const handleButtonTest = () => {
if(appUiState?.showEditorLoadingBar.isShowing){
//TO GO TO A HUNDRED AND DISAPPER
@@ -71,10 +62,33 @@ export const ControlsTopButtons = () => {
}
};
//const handleButtonRender = () => {
// editorEngine?.take_timeline_cam_clip();
//};
const handleObjectPanelTest = () => {
if (appUiState?.currentSceneObject.isShowing) {
dispatchAppUiState({
type: APPUI_ACTION_TYPES.HIDE_CONTROLPANELS_SCENEOBJECT
});
}else{
dispatchAppUiState({
type:APPUI_ACTION_TYPES.SHOW_CONTROLPANELS_SCENEOBJECT,
payload:{
currentSceneObject:{
objectVectors: {
position: {x:1,y:2,z:3},
rotation: {x:4,y:5,z:6},
scalar: {x:7,y:8,z:9},
}
}
}
});
}
}
const handleButtonCameraView = () => {
editorEngine?.switchCameraView();
};
const handleButtonPlayBack = () => {
editorEngine?.startPlayback();
};
const handleButtonRender = () => {
editorEngine?.generateVideo();
};
@@ -83,12 +97,7 @@ export const ControlsTopButtons = () => {
editorEngine?.take_timeline_cam_clip();
};
// const handleButtonCameraView = () => {
// editorEngine?.change_camera_view();
// };
const handleButtonLoad = () => {};
// const handleButtonRender = () => {};
return (
<div className="flex flex-col gap-2 pl-3 pt-3">
@@ -147,6 +156,12 @@ export const ControlsTopButtons = () => {
<Button variant="secondary" onClick={handleButtonCameraView}>
Toggle Camera View
</Button>
<Button
onClick={handleObjectPanelTest}
className="bg-brand-tertiary hover:bg-brand-teriary-400 focus-visible:outline-brand-tertiary"
>
Show Object Panel
</Button>
</div>
<div className="flex gap-2">
@@ -0,0 +1,11 @@
export type XYZ = {
x :number;
y: number;
z: number;
}
export type Simple3DVector = {
position: XYZ;
rotation: XYZ;
scalar: XYZ;
}
@@ -2,6 +2,33 @@ import { State, Action, ACTION_TYPES} from './types';
export function reducer(state: State, action: Action): State {
switch(action.type){
case ACTION_TYPES.SHOW_CONTROLPANELS_SCENEOBJECT:{
return{
...state,
currentSceneObject: {
isShowing: true,
objectVectors: {...action.payload.currentSceneObject.objectVectors},
},
}
}
case ACTION_TYPES.UPDATE_CONTROLPANELS_SCENEOBJECT:{
return{
...state,
currentSceneObject: {
isShowing: state.currentSceneObject.isShowing,
objectVectors: {...action.payload.currentSceneObject.objectVectors},
},
}
}
case ACTION_TYPES.HIDE_CONTROLPANELS_SCENEOBJECT:{
return{
...state,
currentSceneObject: {
isShowing: false,
objectVectors: state.currentSceneObject.objectVectors,
},
}
}
case ACTION_TYPES.SHOW_EDITOR_LOADER:{
return{
...state,
@@ -1,3 +1,4 @@
import { Simple3DVector } from "../../datastructures/common";
export enum VIEW_MODES {
EDITOR = 'editor',
SIDE_BY_SIDE = 'side-by-side'
@@ -19,6 +20,10 @@ export type State = {
progress?: number;
useFakeTimer?: number;
}
currentSceneObject:{
isShowing: boolean;
objectVectors: Simple3DVector;
}
};
@@ -30,9 +35,29 @@ export enum ACTION_TYPES {
SHOW_EDITOR_LOADINGBAR = "show_editor_loadingbar",
UPDATE_EDITOR_LOADINGBAR = "update_editor_loadingbar",
HIDE_EDITOR_LOADINGBAR = "hide_editor_loadingbar",
SHOW_CONTROLPANELS_SCENEOBJECT = "show_controlpanels_sceneobject",
UPDATE_CONTROLPANELS_SCENEOBJECT = "update_controlpanels_sceneobject",
HIDE_CONTROLPANELS_SCENEOBJECT = "hide_controlpanels_sceneobject",
};
export type Action =
export type Action =
| {type: ACTION_TYPES.HIDE_CONTROLPANELS_SCENEOBJECT}
| {
type: ACTION_TYPES.UPDATE_CONTROLPANELS_SCENEOBJECT,
payload:{
currentSceneObject: {
objectVectors: Simple3DVector;
};
}
}
| {
type: ACTION_TYPES.SHOW_CONTROLPANELS_SCENEOBJECT,
payload:{
currentSceneObject: {
objectVectors: Simple3DVector;
};
}
}
| {type: ACTION_TYPES.HIDE_EDITOR_LOADER,}
| {
type: ACTION_TYPES.SHOW_EDITOR_LOADER,
@@ -11,5 +11,13 @@ export const initialStateValues : State = {
isShowing:true,
progress:5,
message: 'Loading Editor Engine 🦊'
},
currentSceneObject:{
isShowing:false,
objectVectors:{
position:{x:0,y:0,z:0},
rotation:{x:0,y:0,z:0},
scalar:{x:0,y:0,z:0},
}
}
}
+8 -1
View File
@@ -126,9 +126,16 @@ export default {
"ui-controls-button": "#676781",
"ui-divider": "#515168",
"axis-x" :"#D33242",
"axis-y" :"#308752",
"axis-z" :"#2E70FF",
red: "#D33242",
green:"#308752",
blue: "#2E70FF",
white: colors.white,
gray: colors.gray,
black: colors.black,
white: colors.white,
},
},
plugins: [],