Skip to content

Commit

Permalink
More recipe types, Camera Controls
Browse files Browse the repository at this point in the history
  • Loading branch information
shartte committed Jul 9, 2023
1 parent ea1054f commit 93db53b
Show file tree
Hide file tree
Showing 28 changed files with 607 additions and 224 deletions.
1 change: 1 addition & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ module.exports = {
rules: {
'react-refresh/only-export-components': 'warn',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-empty-function': 'off',
},
}
Binary file added src/assets/button_invisible.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/button_minus.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/button_plus.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/button_reset.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/button_visible.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/entropy_cool.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/entropy_heat.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/inscriber_arrows_bg_light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/large_slot_light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/recipe_arrow_filled_light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/recipe_arrow_light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified src/assets/slot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/slot_cross.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
55 changes: 45 additions & 10 deletions src/components/model-viewer/ModelViewerInternal.module.css
Original file line number Diff line number Diff line change
@@ -1,31 +1,66 @@

.root {
position: relative;
.wrapper {
display: inline-flex;
flex-direction: row;

--modelviewer-width: auto;
--modelviewer-height: auto;
--modelviewer-aspect-ratio: auto;
width: var(--modelviewer-width);
height: var(--modelviewer-height);

background: rgb(0 0 0 / 20%);
background-color: var(--color--scene-background);
border-radius: 16px;
}

.loading .controls {
display: none;
}

.controls {
flex: 0 1 auto;
display: flex;
flex-direction: column;
}

.controls button {
appearance: none;
border: none;
background: none;
}

.controls button img {
width: 32px;
height: 32px;
opacity: 0.8;
}

.controls button:hover img {
opacity: 1;
}

.controls button:active img {
opacity: 0.6;
}

.controls button:disabled img {
opacity: 0.2;
}

.root {
position: relative;
}

.root > img {
position: absolute;
width: var(--modelviewer-width);
height: var(--modelviewer-height);
}

.viewport {
display: inline-block;
width: var(--modelviewer-width);
height: var(--modelviewer-height);
}

.root, .root > img, .root > .viewport {
width: calc(min(100%, var(--modelviewer-width))) !important;
width: var(--modelviewer-width);
height: auto !important;
aspect-ratio: var(--modelviewer-aspect-ratio);
overflow: hidden;
}

143 changes: 119 additions & 24 deletions src/components/model-viewer/ModelViewerInternal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ import { buildInWorldAnnotation } from "./buildInWorldAnnotation.ts";
import addLevelLighting from "./addSceneLighting.ts";
import buildOverlayAnnotation from "./buildOverlayAnnotations.ts";
import TextureManager from "./TextureManager.ts";
import plusIcon from "../../assets/button_plus.png";
import minusIcon from "../../assets/button_minus.png";
import resetIcon from "../../assets/button_reset.png";

const DEBUG = false;

Expand All @@ -41,6 +44,23 @@ declare module "csstype" {
}
}

interface ControlInterface {
zoomIn(): void;

zoomOut(): void;

resetView(): void;

dispose(): void;
}

const DummyControlInterface: ControlInterface = {
dispose(): void {},
resetView(): void {},
zoomIn(): void {},
zoomOut(): void {},
};

export type Annotation = OverlayAnnotation | InWorldAnnotation;

export type OverlayAnnotation = {
Expand Down Expand Up @@ -118,7 +138,7 @@ async function initialize(
setTooltipObject: (object: ReactNode | undefined) => void,
abortSignal: AbortSignal,
originalWidth: number
) {
): Promise<ControlInterface> {
const renderer = new THREE.WebGLRenderer({
alpha: true,
premultipliedAlpha: false,
Expand Down Expand Up @@ -213,6 +233,7 @@ async function initialize(
let controls: OrbitControls | undefined;
if (cameraControls) {
controls = new OrbitControls(camera, viewportEl);
controls.enableZoom = false;
controls.update();
} else {
camera.lookAt(new Vector3());
Expand All @@ -232,6 +253,7 @@ async function initialize(
if (cameraControls) {
controls?.dispose();
controls = new OrbitControls(camera, viewportEl);
controls.enableZoom = false;
controls.update();
} else {
camera.lookAt(new Vector3());
Expand Down Expand Up @@ -273,18 +295,53 @@ async function initialize(
console.error("Failed to render %s", source, e);
}

return () => {
if (!disposed) {
console.debug("Disposing model viewer for %s", source);
disposed = true;
if (resizeObserver) {
resizeObserver.disconnect();
return {
dispose(): void {
if (!disposed) {
console.debug("Disposing model viewer for %s", source);
disposed = true;
if (resizeObserver) {
resizeObserver.disconnect();
}
viewportEl.removeChild(renderer.domElement);
renderer.dispose();
controls?.dispose();
setTooltipObject(undefined);
}
viewportEl.removeChild(renderer.domElement);
renderer.dispose();
controls?.dispose();
setTooltipObject(undefined);
}
},
resetView(): void {
controls?.reset();
},
zoomIn(): void {
if (controls) {
controls.enableZoom = true;
try {
for (let i = 0; i < 5; i++) {
const e = new WheelEvent("wheel", {
deltaY: -120,
});
controls.domElement.dispatchEvent(e);
}
} finally {
controls.enableZoom = false;
}
}
},
zoomOut(): void {
if (controls) {
controls.enableZoom = true;
try {
for (let i = 0; i < 5; i++) {
const e = new WheelEvent("wheel", {
deltaY: 120,
});
controls.domElement.dispatchEvent(e);
}
} finally {
controls.enableZoom = false;
}
}
},
};
}

Expand All @@ -293,7 +350,7 @@ function ModelViewerInternal({
src,
placeholder,
interactive = false,
background = "transparent",
background,
width,
height,
inWorldAnnotations,
Expand All @@ -304,6 +361,7 @@ function ModelViewerInternal({
const [tooltipObject, setTooltipObject] = useState<ReactNode | undefined>();
const mousePos = useRef<Vector2 | null>(null);
const viewportRef = useRef<HTMLDivElement>(null);
const controlRef = useRef<ControlInterface>(DummyControlInterface);
useLayoutEffect(() => {
const viewportEl = viewportRef.current;
if (!viewportEl) {
Expand All @@ -327,12 +385,13 @@ function ModelViewerInternal({
abortController.signal,
width
)
.then((f) => {
.then((control) => {
if (disposed) {
f();
control.dispose();
} else {
setInitialized(true);
disposer = f;
controlRef.current = control;
disposer = () => control.dispose();
}
})
.catch((err) => {
Expand Down Expand Up @@ -364,6 +423,21 @@ function ModelViewerInternal({
width,
]);

function zoomIn(e: React.MouseEvent) {
e.preventDefault();
controlRef?.current?.zoomIn();
}

function zoomOut(e: React.MouseEvent) {
e.preventDefault();
controlRef?.current?.zoomOut();
}

function resetView(e: React.MouseEvent) {
e.preventDefault();
controlRef?.current?.resetView();
}

function onMouseMove(e: React.MouseEvent<HTMLDivElement>) {
const canvas = viewportRef.current?.querySelector("canvas");
if (!canvas) {
Expand All @@ -386,20 +460,22 @@ function ModelViewerInternal({
}

return (
<>
<div
className={css.wrapper + " " + (!initialized ? css.loading : "")}
style={{
background,
"--modelviewer-width": guiScaledDimension(width),
"--modelviewer-height": guiScaledDimension(height),
"--modelviewer-aspect-ratio": width / height,
}}
>
{error && <ErrorText>{String(error)}</ErrorText>}
<MinecraftTooltip
content={tooltipObject}
visible={Boolean(tooltipObject)}
>
<div
className={css.root}
style={{
background,
"--modelviewer-width": guiScaledDimension(width),
"--modelviewer-height": guiScaledDimension(height),
"--modelviewer-aspect-ratio": width / height,
}}
onMouseMove={onMouseMove}
onMouseLeave={onMouseLeave}
>
Expand All @@ -408,7 +484,26 @@ function ModelViewerInternal({
<div className={css.viewport} ref={viewportRef}></div>
</div>
</MinecraftTooltip>
</>
{interactive && (
<div className={css.controls}>
<MinecraftTooltip content={"Zoom in"}>
<button onClick={zoomIn}>
<img src={plusIcon} alt="" />
</button>
</MinecraftTooltip>
<MinecraftTooltip content={"Zoom out"}>
<button onClick={zoomOut}>
<img src={minusIcon} alt="" />
</button>
</MinecraftTooltip>
<MinecraftTooltip content={"Reset view"}>
<button onClick={resetView}>
<img src={resetIcon} alt="" />
</button>
</MinecraftTooltip>
</div>
)}
</div>
);
}

Expand Down
30 changes: 30 additions & 0 deletions src/components/recipes/ChargerRecipe.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import css from "./recipe.module.css";
import RecipeIngredient from "./RecipeIngredient";
import RecipeArrow from "./RecipeArrow";
import { ChargerRecipeInfo, useGuide } from "../../data/Guide.ts";
import MinecraftFrame from "../MinecraftFrame.tsx";
import ItemIcon from "../ItemIcon.tsx";

export interface ChargerRecipeProps {
recipe: ChargerRecipeInfo;
}

function ChargerRecipe({ recipe }: ChargerRecipeProps) {
const guide = useGuide();
const resultItem = guide.getItemInfo(recipe.resultItem);

return (
<MinecraftFrame>
<div className={css.recipeBoxLayout}>
<div>
<ItemIcon nolink id="charger" /> Charger: {resultItem.displayName}
</div>
<RecipeIngredient itemIds={recipe.ingredient} />
<RecipeArrow />
<RecipeIngredient itemIds={[recipe.resultItem]} />
</div>
</MinecraftFrame>
);
}

export default ChargerRecipe;
5 changes: 4 additions & 1 deletion src/components/recipes/CraftingRecipe.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import RecipeIngredient from "./RecipeIngredient";
import RecipeArrow from "./RecipeArrow";
import { CraftingRecipeInfo, useGuide } from "../../data/Guide.ts";
import MinecraftFrame from "../MinecraftFrame.tsx";
import ItemIcon from "../ItemIcon.tsx";

export interface CraftingRecipeProps {
recipe: CraftingRecipeInfo;
Expand All @@ -17,7 +18,9 @@ function CraftingRecipe({ recipe }: CraftingRecipeProps) {
<MinecraftFrame>
<div className={css.recipeBoxLayout}>
<div title={resultItem.displayName}>
Crafting {recipe.shapeless ? " (Shapeless)" : null}
<ItemIcon nolink id="minecraft:crafting_table" />
Crafting
{recipe.shapeless ? " (Shapeless)" : null}
</div>
<RecipeIngredientGrid {...recipe} />
<RecipeArrow />
Expand Down
5 changes: 4 additions & 1 deletion src/components/recipes/InscriberRecipe.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import RecipeIngredient from "./RecipeIngredient";
import RecipeArrow from "./RecipeArrow";
import { InscriberRecipeInfo, useGuide } from "../../data/Guide.ts";
import MinecraftFrame from "../MinecraftFrame.tsx";
import ItemIcon from "../ItemIcon.tsx";

export interface InscriberRecipeProps {
recipe: InscriberRecipeInfo;
Expand All @@ -15,7 +16,9 @@ function InscriberRecipe({ recipe }: InscriberRecipeProps) {
return (
<MinecraftFrame>
<div className={css.recipeBoxLayout}>
<div>{resultItem.displayName}</div>
<div>
<ItemIcon nolink id="inscriber" /> Inscriber: {resultItem.displayName}
</div>
<div style={{ display: "flex", flexDirection: "column" }}>
<RecipeIngredient itemIds={recipe.top} />
<RecipeIngredient itemIds={recipe.middle} />
Expand Down
Loading

0 comments on commit 93db53b

Please sign in to comment.