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 DiceTrayOverlay from "./dice/DiceTrayOverlay";
import { DiceLoadingProvider } from "../../contexts/DiceLoadingContext";
function MapDice() {
const [isExpanded, setIsExpanded] = useState(false);
@ -32,7 +34,9 @@ function MapDice() {
>
<ExpandMoreDiceIcon isExpanded={isExpanded} />
</IconButton>
<DiceTrayOverlay isOpen={isExpanded} />
<DiceLoadingProvider>
<DiceTrayOverlay isOpen={isExpanded} />
</DiceLoadingProvider>
</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 D20Icon from "../../../icons/D20Icon";
@ -20,10 +20,18 @@ import { dice } from "../../../dice";
function DiceButtons({
diceRolls,
onDiceAdd,
onDiceLoad,
diceTraySize,
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 = {};
for (let dice of diceRolls) {
@ -34,6 +42,11 @@ function DiceButtons({
}
}
async function handleDiceChange(dice) {
await onDiceLoad(dice);
setCurrentDice(dice);
}
return (
<Flex
sx={{
@ -44,7 +57,7 @@ function DiceButtons({
}}
>
<SelectDiceButton
onDiceChange={setCurrentDice}
onDiceChange={handleDiceChange}
currentDice={currentDice}
/>
<Divider vertical color="hsl(210, 50%, 96%)" />

View File

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

View File

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

View File

@ -13,7 +13,7 @@ class IronDice extends Dice {
return { mass: properties.mass * 2, friction: properties.friction };
}
static async createInstance(diceType, scene) {
static async load(scene) {
if (!this.material) {
this.material = this.loadMaterial(
"iron_pbr",
@ -24,6 +24,12 @@ class IronDice extends Dice {
if (!this.meshes) {
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(
this.meshes[diceType],

View File

@ -8,10 +8,10 @@ class NebulaDice extends Dice {
static meshes;
static material;
static async createInstance(diceType, scene) {
static async load(scene) {
if (!this.material) {
this.material = this.loadMaterial(
"nebula_pbr",
"neubula_pbr",
{ albedo, metalRoughness, normal },
scene
);
@ -19,6 +19,12 @@ class NebulaDice extends Dice {
if (!this.meshes) {
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(
this.meshes[diceType],

View File

@ -8,7 +8,7 @@ class SunriseDice extends Dice {
static meshes;
static material;
static async createInstance(diceType, scene) {
static async load(scene) {
if (!this.material) {
this.material = this.loadMaterial(
"sunrise_pbr",
@ -19,6 +19,12 @@ class SunriseDice extends Dice {
if (!this.meshes) {
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(
this.meshes[diceType],

View File

@ -8,7 +8,7 @@ class SunsetDice extends Dice {
static meshes;
static material;
static async createInstance(diceType, scene) {
static async load(scene) {
if (!this.material) {
this.material = this.loadMaterial(
"sunset_pbr",
@ -19,6 +19,12 @@ class SunsetDice extends Dice {
if (!this.meshes) {
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(
this.meshes[diceType],

View File

@ -8,7 +8,7 @@ class WalnutDice extends Dice {
static meshes;
static material;
static async createInstance(diceType, scene) {
static async load(scene) {
if (!this.material) {
this.material = this.loadMaterial(
"walnut_pbr",
@ -19,6 +19,12 @@ class WalnutDice extends Dice {
if (!this.meshes) {
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(
this.meshes[diceType],