diff --git a/src/components/map/MapDice.js b/src/components/map/MapDice.js index 98c0653..fb7b7ae 100644 --- a/src/components/map/MapDice.js +++ b/src/components/map/MapDice.js @@ -2,7 +2,7 @@ import React, { useState } from "react"; import { Flex, IconButton } from "theme-ui"; import ExpandMoreDiceIcon from "../../icons/ExpandMoreDiceIcon"; -import DiceTray from "./dice/DiceTray"; +import DiceTrayOverlay from "./dice/DiceTrayOverlay"; function MapDice() { const [isExpanded, setIsExpanded] = useState(false); @@ -32,7 +32,7 @@ function MapDice() { > - + ); } diff --git a/src/components/map/dice/DiceButtons.js b/src/components/map/dice/DiceButtons.js index 4c611e9..84e9301 100644 --- a/src/components/map/dice/DiceButtons.js +++ b/src/components/map/dice/DiceButtons.js @@ -1,5 +1,5 @@ import React, { useState } from "react"; -import { Flex } from "theme-ui"; +import { Flex, IconButton } from "theme-ui"; import D20Icon from "../../../icons/D20Icon"; import D12Icon from "../../../icons/D12Icon"; @@ -8,6 +8,7 @@ import D8Icon from "../../../icons/D8Icon"; import D6Icon from "../../../icons/D6Icon"; import D4Icon from "../../../icons/D4Icon"; import D100Icon from "../../../icons/D100Icon"; +import ExpandMoreDiceTrayIcon from "../../../icons/ExpandMoreDiceTrayIcon"; import DiceButton from "./DiceButton"; import SelectDiceButton from "./SelectDiceButton"; @@ -16,7 +17,12 @@ import Divider from "../../Divider"; import { dice } from "../../../dice"; -function DiceButtons({ diceRolls, onDiceAdd }) { +function DiceButtons({ + diceRolls, + onDiceAdd, + diceTraySize, + onDiceTraySizeChange, +}) { const [currentDice, setCurrentDice] = useState(dice[0]); const diceCounts = {}; @@ -91,6 +97,23 @@ function DiceButtons({ diceRolls, onDiceAdd }) { > + + + onDiceTraySizeChange(diceTraySize === "single" ? "double" : "single") + } + > + + ); } diff --git a/src/components/map/dice/DiceControls.js b/src/components/map/dice/DiceControls.js index 189422c..df5ee28 100644 --- a/src/components/map/dice/DiceControls.js +++ b/src/components/map/dice/DiceControls.js @@ -10,6 +10,8 @@ function DiceControls({ onDiceAdd, onDiceClear, onDiceReroll, + diceTraySize, + onDiceTraySizeChange, }) { const [diceRolls, setDiceRolls] = useState([]); @@ -114,6 +116,8 @@ function DiceControls({ { type, roll: "unknown" }, ]); }} + onDiceTraySizeChange={onDiceTraySizeChange} + diceTraySize={diceTraySize} /> diff --git a/src/components/map/dice/DiceScene.js b/src/components/map/dice/DiceScene.js index c7c62e3..9e90f9d 100644 --- a/src/components/map/dice/DiceScene.js +++ b/src/components/map/dice/DiceScene.js @@ -74,7 +74,7 @@ function DiceScene({ onSceneMount, onPointerDown, onPointerUp }) { const scene = sceneRef.current; if (scene) { const pickInfo = scene.pick(scene.pointerX, scene.pointerY); - if (pickInfo.hit && pickInfo.pickedMesh.id !== "tray") { + if (pickInfo.hit && pickInfo.pickedMesh.name !== "dice_tray") { pickInfo.pickedMesh.physicsImpostor.setLinearVelocity( BABYLON.Vector3.Zero() ); diff --git a/src/components/map/dice/DiceTray.js b/src/components/map/dice/DiceTrayOverlay.js similarity index 80% rename from src/components/map/dice/DiceTray.js rename to src/components/map/dice/DiceTrayOverlay.js index f76a5cb..5a56592 100644 --- a/src/components/map/dice/DiceTray.js +++ b/src/components/map/dice/DiceTrayOverlay.js @@ -1,4 +1,10 @@ -import React, { useRef, useCallback, useEffect, useContext } from "react"; +import React, { + useRef, + useCallback, + useEffect, + useContext, + useState, +} from "react"; import * as BABYLON from "babylonjs"; import { Box } from "theme-ui"; @@ -8,13 +14,11 @@ import Scene from "./DiceScene"; import DiceControls from "./DiceControls"; import Dice from "../../../dice/Dice"; -import createDiceTray, { - diceTraySize, -} from "../../../dice/diceTray/DiceTrayMesh"; +import DiceTray from "../../../dice/diceTray/DiceTray"; import MapInteractionContext from "../../../contexts/MapInteractionContext"; -function DiceTray({ isOpen }) { +function DiceTrayOverlay({ isOpen }) { const sceneRef = useRef(); const shadowGeneratorRef = useRef(); const diceRefs = useRef([]); @@ -22,6 +26,26 @@ function DiceTray({ isOpen }) { const sceneInteractionRef = useRef(false); // Set to true to ignore scene sleep and visible values const forceSceneRenderRef = useRef(false); + const diceTrayRef = useRef(); + + const [diceTraySize, setDiceTraySize] = useState("single"); + useEffect(() => { + const diceTray = diceTrayRef.current; + let resizeTimout; + if (diceTray) { + diceTray.size = diceTraySize; + // Force rerender + forceSceneRenderRef.current = true; + resizeTimout = setTimeout(() => { + forceSceneRenderRef.current = false; + }, 1000); + } + return () => { + if (resizeTimout) { + clearTimeout(resizeTimout); + } + }; + }, [diceTraySize]); useEffect(() => { let openTimeout; @@ -49,7 +73,7 @@ function DiceTray({ isOpen }) { }, []); async function initializeScene(scene) { - var light = new BABYLON.DirectionalLight( + let light = new BABYLON.DirectionalLight( "DirectionalLight", new BABYLON.Vector3(-0.5, -1, -0.5), scene @@ -62,7 +86,7 @@ function DiceTray({ isOpen }) { shadowGenerator.darkness = 0.7; shadowGeneratorRef.current = shadowGenerator; - var ground = BABYLON.Mesh.CreateGround("ground", 100, 100, 2, scene); + let ground = BABYLON.Mesh.CreateGround("ground", 100, 100, 2, scene); ground.physicsImpostor = new BABYLON.PhysicsImpostor( ground, BABYLON.PhysicsImpostor.BoxImpostor, @@ -72,36 +96,7 @@ function DiceTray({ isOpen }) { ground.isVisible = false; ground.position.y = 0.2; - const wallSize = 50; - - function createWall(name, x, z, yaw) { - let wall = BABYLON.Mesh.CreateBox( - name, - wallSize, - scene, - true, - BABYLON.Mesh.DOUBLESIDE - ); - wall.rotation = new BABYLON.Vector3(0, yaw, 0); - wall.position.z = z; - wall.position.x = x; - wall.physicsImpostor = new BABYLON.PhysicsImpostor( - wall, - BABYLON.PhysicsImpostor.BoxImpostor, - { mass: 0, friction: 10.0 }, - scene - ); - wall.isVisible = false; - } - - const wallOffsetWidth = wallSize / 2 + diceTraySize.width / 2 - 0.5; - const wallOffsetHeight = wallSize / 2 + diceTraySize.height / 2 - 0.5; - createWall("wallTop", 0, -wallOffsetHeight, 0); - createWall("wallRight", -wallOffsetWidth, 0, Math.PI / 2); - createWall("wallBottom", 0, wallOffsetHeight, Math.PI); - createWall("wallLeft", wallOffsetWidth, 0, -Math.PI / 2); - - var roof = BABYLON.Mesh.CreateGround("roof", 100, 100, 2, scene); + let roof = BABYLON.Mesh.CreateGround("roof", 100, 100, 2, scene); roof.physicsImpostor = new BABYLON.PhysicsImpostor( roof, BABYLON.PhysicsImpostor.BoxImpostor, @@ -117,7 +112,9 @@ function DiceTray({ isOpen }) { ); scene.environmentIntensity = 1.0; - createDiceTray(scene, shadowGenerator); + let diceTray = new DiceTray("single", scene, shadowGenerator); + await diceTray.load(); + diceTrayRef.current = diceTray; } function update(scene) { @@ -222,9 +219,12 @@ function DiceTray({ isOpen }) { return ( ); } -export default DiceTray; +export default DiceTrayOverlay; diff --git a/src/components/map/dice/SelectDiceButton.js b/src/components/map/dice/SelectDiceButton.js index 8550667..7f470ec 100644 --- a/src/components/map/dice/SelectDiceButton.js +++ b/src/components/map/dice/SelectDiceButton.js @@ -26,7 +26,12 @@ function SelectDiceButton({ onDiceChange, currentDice }) { return ( <> - + 0.5 ? trayOffsetHeight : -trayOffsetHeight + Math.random() > 0.5 + ? trayBounds.maximumWorld.z + : trayBounds.minimumWorld.z ); instance.position = position; instance.addRotation( diff --git a/src/dice/diceTray/DiceTray.js b/src/dice/diceTray/DiceTray.js new file mode 100644 index 0000000..8fd90c2 --- /dev/null +++ b/src/dice/diceTray/DiceTray.js @@ -0,0 +1,156 @@ +import * as BABYLON from "babylonjs"; + +import singleMeshSource from "../meshes/diceTraySingle.glb"; +import doubleMeshSource from "../meshes/diceTrayDouble.glb"; + +import singleAlbedo from "./singleAlbedo.jpg"; +import singleMetalRoughness from "./singleMetalRoughness.jpg"; +import singleNormal from "./singleNormal.jpg"; + +import doubleAlbedo from "./doubleAlbedo.jpg"; +import doubleMetalRoughness from "./doubleMetalRoughness.jpg"; +import doubleNormal from "./doubleNormal.jpg"; + +class DiceTray { + _size; + get size() { + return this._size; + } + set size(newSize) { + this._size = newSize; + const wallOffsetWidth = this.wallSize / 2 + this.width / 2 - 0.5; + const wallOffsetHeight = this.wallSize / 2 + this.height / 2 - 0.5; + this.wallTop.position.z = -wallOffsetHeight; + this.wallRight.position.x = -wallOffsetWidth; + this.wallBottom.position.z = wallOffsetHeight; + this.wallLeft.position.x = wallOffsetWidth; + this.singleMesh.isVisible = newSize === "single"; + this.doubleMesh.isVisible = newSize === "double"; + } + scene; + shadowGenerator; + get width() { + return this.size === "single" ? 10 : 20; + } + height = 20; + wallSize = 50; + wallTop; + wallRight; + wallBottom; + wallLeft; + singleMesh; + doubleMesh; + + constructor(initialSize, scene, shadowGenerator) { + this._size = initialSize; + this.scene = scene; + this.shadowGenerator = shadowGenerator; + } + + async load() { + this.loadWalls(); + await this.loadMeshes(); + } + + createWall(name, x, z, yaw) { + let wall = BABYLON.Mesh.CreateBox( + name, + this.wallSize, + this.scene, + true, + BABYLON.Mesh.DOUBLESIDE + ); + wall.rotation = new BABYLON.Vector3(0, yaw, 0); + wall.position.z = z; + wall.position.x = x; + wall.physicsImpostor = new BABYLON.PhysicsImpostor( + wall, + BABYLON.PhysicsImpostor.BoxImpostor, + { mass: 0, friction: 10.0 }, + this.scene + ); + wall.isVisible = false; + + return wall; + } + + loadWalls() { + const wallOffsetWidth = this.wallSize / 2 + this.width / 2 - 0.5; + const wallOffsetHeight = this.wallSize / 2 + this.height / 2 - 0.5; + this.wallTop = this.createWall("wallTop", 0, -wallOffsetHeight, 0); + this.wallRight = this.createWall( + "wallRight", + -wallOffsetWidth, + 0, + Math.PI / 2 + ); + this.wallBottom = this.createWall( + "wallBottom", + 0, + wallOffsetHeight, + Math.PI + ); + this.wallLeft = this.createWall( + "wallLeft", + wallOffsetWidth, + 0, + -Math.PI / 2 + ); + } + + async loadMeshes() { + this.singleMesh = ( + await BABYLON.SceneLoader.ImportMeshAsync( + "", + singleMeshSource, + "", + this.scene + ) + ).meshes[1]; + this.singleMesh.id = "dice_tray_single"; + this.singleMesh.name = "dice_tray"; + let singleMaterial = new BABYLON.PBRMaterial( + "dice_tray_mat_single", + this.scene + ); + singleMaterial.albedoTexture = new BABYLON.Texture(singleAlbedo); + singleMaterial.normalTexture = new BABYLON.Texture(singleNormal); + singleMaterial.metallicTexture = new BABYLON.Texture(singleMetalRoughness); + singleMaterial.useRoughnessFromMetallicTextureAlpha = false; + singleMaterial.useRoughnessFromMetallicTextureGreen = true; + singleMaterial.useMetallnessFromMetallicTextureBlue = true; + this.singleMesh.material = singleMaterial; + + this.singleMesh.receiveShadows = true; + this.shadowGenerator.addShadowCaster(this.singleMesh); + this.singleMesh.isVisible = this.size === "single"; + + this.doubleMesh = ( + await BABYLON.SceneLoader.ImportMeshAsync( + "", + doubleMeshSource, + "", + this.scene + ) + ).meshes[1]; + this.doubleMesh.id = "dice_tray_double"; + this.doubleMesh.name = "dice_tray"; + let doubleMaterial = new BABYLON.PBRMaterial( + "dice_tray_mat_double", + this.scene + ); + doubleMaterial.albedoTexture = new BABYLON.Texture(doubleAlbedo); + doubleMaterial.normalTexture = new BABYLON.Texture(doubleNormal); + doubleMaterial.metallicTexture = new BABYLON.Texture(doubleMetalRoughness); + doubleMaterial.useRoughnessFromMetallicTextureAlpha = false; + doubleMaterial.useRoughnessFromMetallicTextureGreen = true; + doubleMaterial.useMetallnessFromMetallicTextureBlue = true; + this.doubleMesh.material = doubleMaterial; + + this.doubleMesh.receiveShadows = true; + this.shadowGenerator.addShadowCaster(this.doubleMesh); + this.doubleMesh.isVisible = this.size === "double"; + } +} + +export default DiceTray; diff --git a/src/dice/diceTray/DiceTrayMesh.js b/src/dice/diceTray/DiceTrayMesh.js deleted file mode 100644 index 5938f5a..0000000 --- a/src/dice/diceTray/DiceTrayMesh.js +++ /dev/null @@ -1,28 +0,0 @@ -import * as BABYLON from "babylonjs"; - -import meshSource from "../meshes/diceTraySingle.glb"; - -import albedo from "./albedo.jpg"; -import metalRoughness from "./metalRoughness.jpg"; -import normal from "./normal.jpg"; - -export const diceTraySize = { width: 10, height: 20 }; - -export default async function createDiceTray(scene, shadowGenerator) { - let mesh = ( - await BABYLON.SceneLoader.ImportMeshAsync("", meshSource, "", scene) - ).meshes[1]; - mesh.id = "tray"; - let material = new BABYLON.PBRMaterial("dice_tray_mat", scene); - material.albedoTexture = new BABYLON.Texture(albedo); - material.normalTexture = new BABYLON.Texture(normal); - material.metallicTexture = new BABYLON.Texture(metalRoughness); - material.useRoughnessFromMetallicTextureAlpha = false; - material.useRoughnessFromMetallicTextureGreen = true; - material.useMetallnessFromMetallicTextureBlue = true; - mesh.material = material; - - mesh.receiveShadows = true; - - shadowGenerator.addShadowCaster(mesh); -} diff --git a/src/dice/diceTray/doubleAlbedo.jpg b/src/dice/diceTray/doubleAlbedo.jpg new file mode 100644 index 0000000..1df822b Binary files /dev/null and b/src/dice/diceTray/doubleAlbedo.jpg differ diff --git a/src/dice/diceTray/doubleMetalRoughness.jpg b/src/dice/diceTray/doubleMetalRoughness.jpg new file mode 100644 index 0000000..efa575d Binary files /dev/null and b/src/dice/diceTray/doubleMetalRoughness.jpg differ diff --git a/src/dice/diceTray/doubleNormal.jpg b/src/dice/diceTray/doubleNormal.jpg new file mode 100644 index 0000000..00ea0a0 Binary files /dev/null and b/src/dice/diceTray/doubleNormal.jpg differ diff --git a/src/dice/diceTray/albedo.jpg b/src/dice/diceTray/singleAlbedo.jpg similarity index 100% rename from src/dice/diceTray/albedo.jpg rename to src/dice/diceTray/singleAlbedo.jpg diff --git a/src/dice/diceTray/metalRoughness.jpg b/src/dice/diceTray/singleMetalRoughness.jpg similarity index 100% rename from src/dice/diceTray/metalRoughness.jpg rename to src/dice/diceTray/singleMetalRoughness.jpg diff --git a/src/dice/diceTray/normal.jpg b/src/dice/diceTray/singleNormal.jpg similarity index 100% rename from src/dice/diceTray/normal.jpg rename to src/dice/diceTray/singleNormal.jpg diff --git a/src/dice/meshes/diceTrayDouble.glb b/src/dice/meshes/diceTrayDouble.glb new file mode 100644 index 0000000..0eb752b Binary files /dev/null and b/src/dice/meshes/diceTrayDouble.glb differ diff --git a/src/icons/ExpandMoreDiceTrayIcon.js b/src/icons/ExpandMoreDiceTrayIcon.js new file mode 100644 index 0000000..2b860d9 --- /dev/null +++ b/src/icons/ExpandMoreDiceTrayIcon.js @@ -0,0 +1,19 @@ +import React from "react"; + +function ExpandMoreDotIcon() { + return ( + + + + + + ); +} + +export default ExpandMoreDotIcon;