Separated dice loading and dice instance creation and added loading spinner for dice

This commit is contained in:
Mitchell McCaffrey 2020-05-27 14:47:51 +10:00
parent da84f923d1
commit 7f0b4e32af
11 changed files with 117 additions and 11 deletions

View File

@ -4,6 +4,8 @@ import { Flex, IconButton } from "theme-ui";
import ExpandMoreDiceIcon from "../../icons/ExpandMoreDiceIcon"; import ExpandMoreDiceIcon from "../../icons/ExpandMoreDiceIcon";
import DiceTrayOverlay from "./dice/DiceTrayOverlay"; import DiceTrayOverlay from "./dice/DiceTrayOverlay";
import { DiceLoadingProvider } from "../../contexts/DiceLoadingContext";
function MapDice() { function MapDice() {
const [isExpanded, setIsExpanded] = useState(false); const [isExpanded, setIsExpanded] = useState(false);
@ -32,7 +34,9 @@ function MapDice() {
> >
<ExpandMoreDiceIcon isExpanded={isExpanded} /> <ExpandMoreDiceIcon isExpanded={isExpanded} />
</IconButton> </IconButton>
<DiceLoadingProvider>
<DiceTrayOverlay isOpen={isExpanded} /> <DiceTrayOverlay isOpen={isExpanded} />
</DiceLoadingProvider>
</Flex> </Flex>
); );
} }

View File

@ -1,4 +1,4 @@
import React, { useState } from "react"; import React, { useState, useEffect } from "react";
import { Flex, IconButton } from "theme-ui"; import { Flex, IconButton } from "theme-ui";
import D20Icon from "../../../icons/D20Icon"; import D20Icon from "../../../icons/D20Icon";
@ -20,10 +20,18 @@ import { dice } from "../../../dice";
function DiceButtons({ function DiceButtons({
diceRolls, diceRolls,
onDiceAdd, onDiceAdd,
onDiceLoad,
diceTraySize, diceTraySize,
onDiceTraySizeChange, onDiceTraySizeChange,
}) { }) {
const [currentDice, setCurrentDice] = useState(dice[0]); const [currentDice, setCurrentDice] = useState();
useEffect(() => {
const initialDice = dice[0];
onDiceLoad(initialDice);
setCurrentDice(initialDice);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const diceCounts = {}; const diceCounts = {};
for (let dice of diceRolls) { for (let dice of diceRolls) {
@ -34,6 +42,11 @@ function DiceButtons({
} }
} }
async function handleDiceChange(dice) {
await onDiceLoad(dice);
setCurrentDice(dice);
}
return ( return (
<Flex <Flex
sx={{ sx={{
@ -44,7 +57,7 @@ function DiceButtons({
}} }}
> >
<SelectDiceButton <SelectDiceButton
onDiceChange={setCurrentDice} onDiceChange={handleDiceChange}
currentDice={currentDice} currentDice={currentDice}
/> />
<Divider vertical color="hsl(210, 50%, 96%)" /> <Divider vertical color="hsl(210, 50%, 96%)" />

View File

@ -10,6 +10,7 @@ function DiceControls({
onDiceAdd, onDiceAdd,
onDiceClear, onDiceClear,
onDiceReroll, onDiceReroll,
onDiceLoad,
diceTraySize, diceTraySize,
onDiceTraySizeChange, onDiceTraySizeChange,
}) { }) {
@ -116,6 +117,7 @@ function DiceControls({
{ type, roll: "unknown" }, { type, roll: "unknown" },
]); ]);
}} }}
onDiceLoad={onDiceLoad}
onDiceTraySizeChange={onDiceTraySizeChange} onDiceTraySizeChange={onDiceTraySizeChange}
diceTraySize={diceTraySize} diceTraySize={diceTraySize}
/> />

View File

@ -13,10 +13,12 @@ import environment from "../../../dice/environment.dds";
import Scene from "./DiceScene"; import Scene from "./DiceScene";
import DiceControls from "./DiceControls"; import DiceControls from "./DiceControls";
import Dice from "../../../dice/Dice"; import Dice from "../../../dice/Dice";
import LoadingOverlay from "../../LoadingOverlay";
import DiceTray from "../../../dice/diceTray/DiceTray"; import DiceTray from "../../../dice/diceTray/DiceTray";
import MapInteractionContext from "../../../contexts/MapInteractionContext"; import MapInteractionContext from "../../../contexts/MapInteractionContext";
import DiceLoadingContext from "../../../contexts/DiceLoadingContext";
function DiceTrayOverlay({ isOpen }) { function DiceTrayOverlay({ isOpen }) {
const sceneRef = useRef(); const sceneRef = useRef();
@ -29,6 +31,10 @@ function DiceTrayOverlay({ isOpen }) {
const diceTrayRef = useRef(); const diceTrayRef = useRef();
const [diceTraySize, setDiceTraySize] = useState("single"); const [diceTraySize, setDiceTraySize] = useState("single");
const { assetLoadStart, assetLoadFinish, isLoading } = useContext(
DiceLoadingContext
);
useEffect(() => { useEffect(() => {
const diceTray = diceTrayRef.current; const diceTray = diceTrayRef.current;
let resizeTimout; let resizeTimout;
@ -70,9 +76,11 @@ function DiceTrayOverlay({ isOpen }) {
sceneRef.current = scene; sceneRef.current = scene;
initializeScene(scene); initializeScene(scene);
engine.runRenderLoop(() => update(scene)); engine.runRenderLoop(() => update(scene));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
async function initializeScene(scene) { async function initializeScene(scene) {
assetLoadStart();
let light = new BABYLON.DirectionalLight( let light = new BABYLON.DirectionalLight(
"DirectionalLight", "DirectionalLight",
new BABYLON.Vector3(-0.5, -1, -0.5), new BABYLON.Vector3(-0.5, -1, -0.5),
@ -95,6 +103,7 @@ function DiceTrayOverlay({ isOpen }) {
let diceTray = new DiceTray("single", scene, shadowGenerator); let diceTray = new DiceTray("single", scene, shadowGenerator);
await diceTray.load(); await diceTray.load();
diceTrayRef.current = diceTray; diceTrayRef.current = diceTray;
assetLoadFinish();
} }
function update(scene) { function update(scene) {
@ -194,6 +203,15 @@ function DiceTrayOverlay({ isOpen }) {
} }
} }
async function handleDiceLoad(dice) {
assetLoadStart();
const scene = sceneRef.current;
if (scene) {
await dice.class.load(scene);
}
assetLoadFinish();
}
const { setPreventMapInteraction } = useContext(MapInteractionContext); const { setPreventMapInteraction } = useContext(MapInteractionContext);
return ( return (
@ -229,9 +247,11 @@ function DiceTrayOverlay({ isOpen }) {
onDiceAdd={handleDiceAdd} onDiceAdd={handleDiceAdd}
onDiceClear={handleDiceClear} onDiceClear={handleDiceClear}
onDiceReroll={handleDiceReroll} onDiceReroll={handleDiceReroll}
onDiceLoad={handleDiceLoad}
diceTraySize={diceTraySize} diceTraySize={diceTraySize}
onDiceTraySizeChange={setDiceTraySize} onDiceTraySizeChange={setDiceTraySize}
/> />
{isLoading && <LoadingOverlay />}
</Box> </Box>
); );
} }

View File

@ -0,0 +1,31 @@
import React, { useState } from "react";
const DiceLoadingContext = React.createContext();
export function DiceLoadingProvider({ children }) {
const [loadingAssetCount, setLoadingAssetCount] = useState(0);
function assetLoadStart() {
setLoadingAssetCount((prevLoadingAssets) => prevLoadingAssets + 1);
}
function assetLoadFinish() {
setLoadingAssetCount((prevLoadingAssets) => prevLoadingAssets - 1);
}
const isLoading = loadingAssetCount > 0;
const value = {
assetLoadStart,
assetLoadFinish,
isLoading,
};
return (
<DiceLoadingContext.Provider value={value}>
{children}
</DiceLoadingContext.Provider>
);
}
export default DiceLoadingContext;

View File

@ -8,7 +8,7 @@ class GalaxyDice extends Dice {
static meshes; static meshes;
static material; static material;
static async createInstance(diceType, scene) { static async load(scene) {
if (!this.material) { if (!this.material) {
this.material = this.loadMaterial( this.material = this.loadMaterial(
"galaxy_pbr", "galaxy_pbr",
@ -19,6 +19,12 @@ class GalaxyDice extends Dice {
if (!this.meshes) { if (!this.meshes) {
this.meshes = await this.loadMeshes(this.material, scene); this.meshes = await this.loadMeshes(this.material, scene);
} }
}
static async createInstance(diceType, scene) {
if (!this.material || !this.meshes) {
throw Error("Dice not loaded, call load before creating an instance");
}
return Dice.createInstance( return Dice.createInstance(
this.meshes[diceType], this.meshes[diceType],

View File

@ -13,7 +13,7 @@ class IronDice extends Dice {
return { mass: properties.mass * 2, friction: properties.friction }; return { mass: properties.mass * 2, friction: properties.friction };
} }
static async createInstance(diceType, scene) { static async load(scene) {
if (!this.material) { if (!this.material) {
this.material = this.loadMaterial( this.material = this.loadMaterial(
"iron_pbr", "iron_pbr",
@ -24,6 +24,12 @@ class IronDice extends Dice {
if (!this.meshes) { if (!this.meshes) {
this.meshes = await this.loadMeshes(this.material, scene); this.meshes = await this.loadMeshes(this.material, scene);
} }
}
static async createInstance(diceType, scene) {
if (!this.material || !this.meshes) {
throw Error("Dice not loaded, call load before creating an instance");
}
return Dice.createInstance( return Dice.createInstance(
this.meshes[diceType], this.meshes[diceType],

View File

@ -8,10 +8,10 @@ class NebulaDice extends Dice {
static meshes; static meshes;
static material; static material;
static async createInstance(diceType, scene) { static async load(scene) {
if (!this.material) { if (!this.material) {
this.material = this.loadMaterial( this.material = this.loadMaterial(
"nebula_pbr", "neubula_pbr",
{ albedo, metalRoughness, normal }, { albedo, metalRoughness, normal },
scene scene
); );
@ -19,6 +19,12 @@ class NebulaDice extends Dice {
if (!this.meshes) { if (!this.meshes) {
this.meshes = await this.loadMeshes(this.material, scene); this.meshes = await this.loadMeshes(this.material, scene);
} }
}
static async createInstance(diceType, scene) {
if (!this.material || !this.meshes) {
throw Error("Dice not loaded, call load before creating an instance");
}
return Dice.createInstance( return Dice.createInstance(
this.meshes[diceType], this.meshes[diceType],

View File

@ -8,7 +8,7 @@ class SunriseDice extends Dice {
static meshes; static meshes;
static material; static material;
static async createInstance(diceType, scene) { static async load(scene) {
if (!this.material) { if (!this.material) {
this.material = this.loadMaterial( this.material = this.loadMaterial(
"sunrise_pbr", "sunrise_pbr",
@ -19,6 +19,12 @@ class SunriseDice extends Dice {
if (!this.meshes) { if (!this.meshes) {
this.meshes = await this.loadMeshes(this.material, scene); this.meshes = await this.loadMeshes(this.material, scene);
} }
}
static async createInstance(diceType, scene) {
if (!this.material || !this.meshes) {
throw Error("Dice not loaded, call load before creating an instance");
}
return Dice.createInstance( return Dice.createInstance(
this.meshes[diceType], this.meshes[diceType],

View File

@ -8,7 +8,7 @@ class SunsetDice extends Dice {
static meshes; static meshes;
static material; static material;
static async createInstance(diceType, scene) { static async load(scene) {
if (!this.material) { if (!this.material) {
this.material = this.loadMaterial( this.material = this.loadMaterial(
"sunset_pbr", "sunset_pbr",
@ -19,6 +19,12 @@ class SunsetDice extends Dice {
if (!this.meshes) { if (!this.meshes) {
this.meshes = await this.loadMeshes(this.material, scene); this.meshes = await this.loadMeshes(this.material, scene);
} }
}
static async createInstance(diceType, scene) {
if (!this.material || !this.meshes) {
throw Error("Dice not loaded, call load before creating an instance");
}
return Dice.createInstance( return Dice.createInstance(
this.meshes[diceType], this.meshes[diceType],

View File

@ -8,7 +8,7 @@ class WalnutDice extends Dice {
static meshes; static meshes;
static material; static material;
static async createInstance(diceType, scene) { static async load(scene) {
if (!this.material) { if (!this.material) {
this.material = this.loadMaterial( this.material = this.loadMaterial(
"walnut_pbr", "walnut_pbr",
@ -19,6 +19,12 @@ class WalnutDice extends Dice {
if (!this.meshes) { if (!this.meshes) {
this.meshes = await this.loadMeshes(this.material, scene); this.meshes = await this.loadMeshes(this.material, scene);
} }
}
static async createInstance(diceType, scene) {
if (!this.material || !this.meshes) {
throw Error("Dice not loaded, call load before creating an instance");
}
return Dice.createInstance( return Dice.createInstance(
this.meshes[diceType], this.meshes[diceType],