Added babylonjs, ammojs, dice models and simple dice interaction
@ -8,6 +8,9 @@
|
||||
"@testing-library/jest-dom": "^4.2.4",
|
||||
"@testing-library/react": "^9.3.2",
|
||||
"@testing-library/user-event": "^7.1.2",
|
||||
"ammo.js": "kripken/ammo.js#aab297a4164779c3a9d8dc8d9da26958de3cb778",
|
||||
"babylonjs": "^4.1.0",
|
||||
"babylonjs-loaders": "^4.1.0",
|
||||
"dexie": "^2.0.4",
|
||||
"fake-indexeddb": "^3.0.0",
|
||||
"interactjs": "^1.9.7",
|
||||
|
@ -7,6 +7,8 @@ import MapToken from "./MapToken";
|
||||
import MapDrawing from "./MapDrawing";
|
||||
import MapFog from "./MapFog";
|
||||
import MapControls from "./MapControls";
|
||||
import MapDice from "./MapDice";
|
||||
import LoadingOverlay from "../LoadingOverlay";
|
||||
|
||||
import { omit } from "../../helpers/shared";
|
||||
import useDataSource from "../../helpers/useDataSource";
|
||||
@ -296,8 +298,13 @@ function Map({
|
||||
map={map}
|
||||
aspectRatio={aspectRatio}
|
||||
isEnabled={selectedToolId === "pan"}
|
||||
controls={mapControls}
|
||||
loading={loading}
|
||||
sideContent={
|
||||
<>
|
||||
<MapDice />
|
||||
{mapControls}
|
||||
{loading && <LoadingOverlay />}
|
||||
</>
|
||||
}
|
||||
>
|
||||
{map && mapImage}
|
||||
{map && mapDrawing}
|
||||
|
194
src/components/map/MapDice.js
Normal file
@ -0,0 +1,194 @@
|
||||
import React, { useRef, useState, useCallback } from "react";
|
||||
import * as BABYLON from "babylonjs";
|
||||
import { Box } from "theme-ui";
|
||||
|
||||
import environment from "../../dice/environment.dds";
|
||||
|
||||
import ColorDice from "../../dice/color/ColorDice";
|
||||
import GemStoneDice from "../../dice/gemStone/GemStoneDice";
|
||||
import GlassDice from "../../dice/glass/GlassDice";
|
||||
import MetalDice from "../../dice/metal/MetalDice";
|
||||
import MetalStoneDice from "../../dice/metalStone/MetalStoneDice";
|
||||
import WoodDice from "../../dice/wood/WoodDice";
|
||||
|
||||
import Scene from "./MapDiceScene";
|
||||
|
||||
function MapDice() {
|
||||
const sceneRef = useRef();
|
||||
const dieRef = useRef([]);
|
||||
const dieSleepRef = useRef([]);
|
||||
const [dieNumbers, setDieNumbers] = useState([]);
|
||||
|
||||
const handleSceneMount = useCallback(({ scene, engine }) => {
|
||||
sceneRef.current = scene;
|
||||
initializeScene(scene);
|
||||
engine.runRenderLoop(() => update(scene));
|
||||
}, []);
|
||||
|
||||
async function initializeScene(scene) {
|
||||
var ground = BABYLON.Mesh.CreateGround("ground", 100, 100, 2, scene);
|
||||
ground.physicsImpostor = new BABYLON.PhysicsImpostor(
|
||||
ground,
|
||||
BABYLON.PhysicsImpostor.BoxImpostor,
|
||||
{ mass: 0, friction: 100.0 },
|
||||
scene
|
||||
);
|
||||
ground.isVisible = false;
|
||||
|
||||
function createWall(name, x, z, yaw) {
|
||||
let wall = BABYLON.Mesh.CreateBox(
|
||||
name,
|
||||
50,
|
||||
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: 1.0 },
|
||||
scene
|
||||
);
|
||||
wall.isVisible = false;
|
||||
}
|
||||
|
||||
createWall("wallTop", 0, -35, 0);
|
||||
createWall("wallRight", -39, 0, Math.PI / 2);
|
||||
createWall("wallBottom", 0, 35, Math.PI);
|
||||
createWall("wallLeft", 39, 0, -Math.PI / 2);
|
||||
|
||||
var roof = BABYLON.Mesh.CreateGround("roof", 100, 100, 2, scene);
|
||||
roof.physicsImpostor = new BABYLON.PhysicsImpostor(
|
||||
roof,
|
||||
BABYLON.PhysicsImpostor.BoxImpostor,
|
||||
{ mass: 0, friction: 1.0 },
|
||||
scene
|
||||
);
|
||||
roof.position.y = 10;
|
||||
roof.isVisible = false;
|
||||
|
||||
scene.environmentTexture = BABYLON.CubeTexture.CreateFromPrefilteredData(
|
||||
environment,
|
||||
scene
|
||||
);
|
||||
scene.environmentIntensity = 1.5;
|
||||
}
|
||||
|
||||
function update(scene) {
|
||||
const die = dieRef.current;
|
||||
for (let i = 0; i < die.length; i++) {
|
||||
const dice = die[i];
|
||||
const diceAsleep = dieSleepRef.current[i];
|
||||
const speed = dice.physicsImpostor.getLinearVelocity().length();
|
||||
if (speed < 0.01 && !diceAsleep) {
|
||||
let highestDot = -1;
|
||||
let highestLocator;
|
||||
for (let locator of dice.getChildTransformNodes()) {
|
||||
let dif = locator
|
||||
.getAbsolutePosition()
|
||||
.subtract(dice.getAbsolutePosition());
|
||||
let direction = dif.normalize();
|
||||
const dot = BABYLON.Vector3.Dot(direction, BABYLON.Vector3.Up());
|
||||
if (dot > highestDot) {
|
||||
highestDot = dot;
|
||||
highestLocator = locator;
|
||||
}
|
||||
}
|
||||
dieSleepRef.current[i] = true;
|
||||
const newNumber = parseInt(highestLocator.name.slice(8));
|
||||
setDieNumbers((prevNumbers) => {
|
||||
let newNumbers = [...prevNumbers];
|
||||
newNumbers[i] = newNumber;
|
||||
return newNumbers;
|
||||
});
|
||||
} else if (speed > 0.5 && diceAsleep) {
|
||||
dieSleepRef.current[i] = false;
|
||||
setDieNumbers((prevNumbers) => {
|
||||
let newNumbers = [...prevNumbers];
|
||||
newNumbers[i] = null;
|
||||
return newNumbers;
|
||||
});
|
||||
}
|
||||
}
|
||||
if (scene) {
|
||||
scene.render();
|
||||
}
|
||||
}
|
||||
|
||||
async function handleAddDice(style, type) {
|
||||
const scene = sceneRef.current;
|
||||
if (scene) {
|
||||
const instance = await style.createInstance(type, scene);
|
||||
dieRef.current.push(instance);
|
||||
dieSleepRef.current.push(false);
|
||||
setDieNumbers((prevNumbers) => [...prevNumbers, null]);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
}}
|
||||
>
|
||||
<Scene onSceneMount={handleSceneMount} />
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "8px",
|
||||
left: "50%",
|
||||
transform: "translateX(-50%)",
|
||||
display: "flex",
|
||||
color: "white",
|
||||
}}
|
||||
>
|
||||
{dieNumbers.map((num, index) => (
|
||||
<h3 key={index}>
|
||||
{num || "?"}
|
||||
{index === dieNumbers.length - 1 ? "" : "+"}
|
||||
</h3>
|
||||
))}
|
||||
<h3>
|
||||
{dieNumbers.length > 0 &&
|
||||
`= ${dieNumbers.reduce((a, b) => (a || 0) + (b || 0))}`}
|
||||
</h3>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
bottom: "24px",
|
||||
left: "50%",
|
||||
transform: "translateX(-50%)",
|
||||
}}
|
||||
>
|
||||
<button onClick={() => handleAddDice(ColorDice, "d20")}>
|
||||
Add color d20
|
||||
</button>
|
||||
<button onClick={() => handleAddDice(GemStoneDice, "d20")}>
|
||||
Add gem d20
|
||||
</button>
|
||||
<button onClick={() => handleAddDice(GlassDice, "d20")}>
|
||||
Add glass d20
|
||||
</button>
|
||||
<button onClick={() => handleAddDice(MetalDice, "d20")}>
|
||||
Add metal d20
|
||||
</button>
|
||||
<button onClick={() => handleAddDice(MetalStoneDice, "d20")}>
|
||||
Add metal stone d20
|
||||
</button>
|
||||
<button onClick={() => handleAddDice(WoodDice, "d20")}>
|
||||
Add wood d20
|
||||
</button>
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default MapDice;
|
124
src/components/map/MapDiceScene.js
Normal file
@ -0,0 +1,124 @@
|
||||
import React, { useRef, useEffect } from "react";
|
||||
import * as BABYLON from "babylonjs";
|
||||
import * as AMMO from "ammo.js";
|
||||
import "babylonjs-loaders";
|
||||
|
||||
function MapDiceScene({ onSceneMount }) {
|
||||
const sceneRef = useRef();
|
||||
const engineRef = useRef();
|
||||
const canvasRef = useRef();
|
||||
const containerRef = useRef();
|
||||
|
||||
useEffect(() => {
|
||||
const canvas = canvasRef.current;
|
||||
const engine = new BABYLON.Engine(canvas, true, {
|
||||
preserveDrawingBuffer: true,
|
||||
stencil: true,
|
||||
});
|
||||
const scene = new BABYLON.Scene(engine);
|
||||
scene.clearColor = new BABYLON.Color4(0, 0, 0, 0);
|
||||
// Enable physics
|
||||
scene.enablePhysics(
|
||||
new BABYLON.Vector3(0, -98, 0),
|
||||
new BABYLON.AmmoJSPlugin(true, AMMO)
|
||||
);
|
||||
|
||||
let camera = new BABYLON.TargetCamera(
|
||||
"camera",
|
||||
new BABYLON.Vector3(0, 85, 0),
|
||||
scene
|
||||
);
|
||||
camera.fov = 0.25;
|
||||
camera.setTarget(BABYLON.Vector3.Zero());
|
||||
|
||||
onSceneMount && onSceneMount({ scene, engine, canvas });
|
||||
|
||||
engineRef.current = engine;
|
||||
sceneRef.current = scene;
|
||||
|
||||
engine.runRenderLoop(() => {
|
||||
const scene = sceneRef.current;
|
||||
const selectedMesh = selectedMeshRef.current;
|
||||
if (selectedMesh && scene) {
|
||||
const ray = scene.createPickingRay(
|
||||
scene.pointerX,
|
||||
scene.pointerY,
|
||||
BABYLON.Matrix.Identity(),
|
||||
camera
|
||||
);
|
||||
const currentPosition = selectedMesh.getAbsolutePosition();
|
||||
let newPosition = ray.origin.scale(camera.globalPosition.y);
|
||||
newPosition.y = currentPosition.y;
|
||||
const delta = newPosition.subtract(currentPosition);
|
||||
selectedMesh.setAbsolutePosition(newPosition);
|
||||
selectedMeshDeltaPositionRef.current = delta;
|
||||
}
|
||||
});
|
||||
|
||||
const resizeObserver = new ResizeObserver((entries) => {
|
||||
engine.resize();
|
||||
for (let entry of entries) {
|
||||
canvasRef.current.width = entry.contentRect.width;
|
||||
canvasRef.current.height = entry.contentRect.height;
|
||||
}
|
||||
});
|
||||
|
||||
resizeObserver.observe(containerRef.current);
|
||||
|
||||
return () => {
|
||||
resizeObserver.disconnect();
|
||||
};
|
||||
}, [onSceneMount]);
|
||||
|
||||
const selectedMeshRef = useRef();
|
||||
const selectedMeshDeltaPositionRef = useRef();
|
||||
function handlePointerDown() {
|
||||
const scene = sceneRef.current;
|
||||
if (scene) {
|
||||
const pickInfo = scene.pick(scene.pointerX, scene.pointerY);
|
||||
if (pickInfo.hit) {
|
||||
pickInfo.pickedMesh.physicsImpostor.setLinearVelocity(
|
||||
new BABYLON.Vector3(0, 0, 0)
|
||||
);
|
||||
pickInfo.pickedMesh.physicsImpostor.setAngularVelocity(
|
||||
new BABYLON.Vector3(0, 0, 0)
|
||||
);
|
||||
selectedMeshRef.current = pickInfo.pickedMesh;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handlePointerUp() {
|
||||
const selectedMesh = selectedMeshRef.current;
|
||||
const deltaPosition = selectedMeshDeltaPositionRef.current;
|
||||
const scene = sceneRef.current;
|
||||
if (selectedMesh && scene && deltaPosition) {
|
||||
let impulse = deltaPosition.scale(1000 / scene.deltaTime);
|
||||
impulse.scale(5);
|
||||
impulse.y = Math.max(impulse.length() * 0.1, 0.5);
|
||||
selectedMesh.physicsImpostor.applyImpulse(
|
||||
impulse,
|
||||
selectedMesh
|
||||
.getAbsolutePosition()
|
||||
.add(new BABYLON.Vector3(0, Math.random() * 0.5 + 0.5, 0))
|
||||
);
|
||||
}
|
||||
selectedMeshRef.current = null;
|
||||
selectedMeshDeltaPositionRef.current = null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{ width: "100%", height: "100%", overflow: "hidden" }}
|
||||
ref={containerRef}
|
||||
>
|
||||
<canvas
|
||||
onPointerDown={handlePointerDown}
|
||||
onPointerUp={handlePointerUp}
|
||||
ref={canvasRef}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default MapDiceScene;
|
@ -5,8 +5,6 @@ import normalizeWheel from "normalize-wheel";
|
||||
|
||||
import { MapInteractionProvider } from "../../contexts/MapInteractionContext";
|
||||
|
||||
import LoadingOverlay from "../LoadingOverlay";
|
||||
|
||||
const zoomSpeed = -0.001;
|
||||
const minZoom = 0.1;
|
||||
const maxZoom = 5;
|
||||
@ -16,8 +14,7 @@ function MapInteraction({
|
||||
aspectRatio,
|
||||
isEnabled,
|
||||
children,
|
||||
controls,
|
||||
loading,
|
||||
sideContent,
|
||||
}) {
|
||||
const mapContainerRef = useRef();
|
||||
const mapMoveContainerRef = useRef();
|
||||
@ -159,8 +156,7 @@ function MapInteraction({
|
||||
</MapInteractionProvider>
|
||||
</Box>
|
||||
</Box>
|
||||
{controls}
|
||||
{loading && <LoadingOverlay />}
|
||||
{sideContent}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
74
src/dice/Dice.js
Normal file
@ -0,0 +1,74 @@
|
||||
import * as BABYLON from "babylonjs";
|
||||
|
||||
import d20SharpSource from "./meshes/d20Sharp.glb";
|
||||
import d20RoundSource from "./meshes/d20Round.glb";
|
||||
|
||||
class Dice {
|
||||
static instanceCount = 0;
|
||||
|
||||
static async loadMeshes(diceStyle, material, scene) {
|
||||
let meshes = {};
|
||||
const d20Source = diceStyle === "round" ? d20RoundSource : d20SharpSource;
|
||||
const d20Mesh = await this.loadMesh(d20Source, material, scene);
|
||||
meshes.d20 = d20Mesh;
|
||||
return meshes;
|
||||
}
|
||||
|
||||
static async loadMesh(source, material, scene) {
|
||||
let mesh = (
|
||||
await BABYLON.SceneLoader.ImportMeshAsync("", source, "", scene)
|
||||
).meshes[1];
|
||||
mesh.setParent(null);
|
||||
|
||||
mesh.material = material;
|
||||
|
||||
mesh.isVisible = false;
|
||||
return mesh;
|
||||
}
|
||||
|
||||
static loadMaterial(materialName, textures, scene) {
|
||||
let pbr = new BABYLON.PBRMaterial(materialName, scene);
|
||||
pbr.albedoTexture = new BABYLON.Texture(textures.albedo);
|
||||
pbr.normalTexture = new BABYLON.Texture(textures.normal);
|
||||
pbr.metallicTexture = new BABYLON.Texture(textures.metalRoughness);
|
||||
pbr.useRoughnessFromMetallicTextureAlpha = false;
|
||||
pbr.useRoughnessFromMetallicTextureGreen = true;
|
||||
pbr.useMetallnessFromMetallicTextureBlue = true;
|
||||
return pbr;
|
||||
}
|
||||
|
||||
static createInstanceFromMesh(mesh, name, scene) {
|
||||
let instance = mesh.createInstance(name);
|
||||
instance.position = mesh.position;
|
||||
for (let child of mesh.getChildTransformNodes()) {
|
||||
const locator = child.clone();
|
||||
locator.setAbsolutePosition(child.getAbsolutePosition());
|
||||
locator.name = child.name;
|
||||
instance.addChild(locator);
|
||||
}
|
||||
|
||||
instance.physicsImpostor = new BABYLON.PhysicsImpostor(
|
||||
instance,
|
||||
BABYLON.PhysicsImpostor.ConvexHullImpostor,
|
||||
{ mass: 1, friction: 50 },
|
||||
scene
|
||||
);
|
||||
|
||||
// TODO: put in random start position
|
||||
instance.position.y = 5;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
static async createInstance(mesh, scene) {
|
||||
this.instanceCount++;
|
||||
|
||||
return this.createInstanceFromMesh(
|
||||
mesh,
|
||||
`dice_instance_${this.instanceCount}`,
|
||||
scene
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Dice;
|
27
src/dice/color/ColorDice.js
Normal file
@ -0,0 +1,27 @@
|
||||
import Dice from "../Dice";
|
||||
|
||||
import albedo from "./albedo.jpg";
|
||||
import metalRoughness from "./metalRoughness.jpg";
|
||||
import normal from "./normal.jpg";
|
||||
|
||||
class ColorDice extends Dice {
|
||||
static meshes;
|
||||
static material;
|
||||
|
||||
static async createInstance(diceType, scene) {
|
||||
if (!this.material) {
|
||||
this.material = this.loadMaterial(
|
||||
"color_pbr",
|
||||
{ albedo, metalRoughness, normal },
|
||||
scene
|
||||
);
|
||||
}
|
||||
if (!this.meshes) {
|
||||
this.meshes = await this.loadMeshes("round", this.material, scene);
|
||||
}
|
||||
|
||||
return Dice.createInstance(this.meshes[diceType], scene);
|
||||
}
|
||||
}
|
||||
|
||||
export default ColorDice;
|
BIN
src/dice/color/albedo.jpg
Normal file
After Width: | Height: | Size: 673 KiB |
BIN
src/dice/color/metalRoughness.jpg
Normal file
After Width: | Height: | Size: 365 KiB |
BIN
src/dice/color/normal.jpg
Normal file
After Width: | Height: | Size: 257 KiB |
BIN
src/dice/environment.dds
Executable file
48
src/dice/gemStone/GemStoneDice.js
Normal file
@ -0,0 +1,48 @@
|
||||
import * as BABYLON from "babylonjs";
|
||||
|
||||
import Dice from "../Dice";
|
||||
|
||||
import albedo from "./albedo.jpg";
|
||||
import metalRoughness from "./metalRoughness.jpg";
|
||||
import normal from "./normal.jpg";
|
||||
|
||||
class GemStoneDice extends Dice {
|
||||
static meshes;
|
||||
static material;
|
||||
|
||||
static loadMaterial(materialName, textures, scene) {
|
||||
let pbr = new BABYLON.PBRMaterial(materialName, scene);
|
||||
|
||||
pbr.albedoTexture = new BABYLON.Texture(textures.albedo);
|
||||
pbr.normalTexture = new BABYLON.Texture(textures.normal);
|
||||
pbr.metallicTexture = new BABYLON.Texture(textures.metalRoughness);
|
||||
pbr.useRoughnessFromMetallicTextureAlpha = false;
|
||||
pbr.useRoughnessFromMetallicTextureGreen = true;
|
||||
pbr.useMetallnessFromMetallicTextureBlue = true;
|
||||
|
||||
pbr.subSurface.isTranslucencyEnabled = true;
|
||||
pbr.subSurface.translucencyIntensity = 1.0;
|
||||
pbr.subSurface.minimumThickness = 5;
|
||||
pbr.subSurface.maximumThickness = 10;
|
||||
pbr.subSurface.tintColor = new BABYLON.Color3(0, 1, 0);
|
||||
|
||||
return pbr;
|
||||
}
|
||||
|
||||
static async createInstance(diceType, scene) {
|
||||
if (!this.material) {
|
||||
this.material = this.loadMaterial(
|
||||
"gem_stone_pbr",
|
||||
{ albedo, metalRoughness, normal },
|
||||
scene
|
||||
);
|
||||
}
|
||||
if (!this.meshes) {
|
||||
this.meshes = await this.loadMeshes("round", this.material, scene);
|
||||
}
|
||||
|
||||
return Dice.createInstance(this.meshes[diceType], scene);
|
||||
}
|
||||
}
|
||||
|
||||
export default GemStoneDice;
|
BIN
src/dice/gemStone/albedo.jpg
Normal file
After Width: | Height: | Size: 493 KiB |
BIN
src/dice/gemStone/metalRoughness.jpg
Normal file
After Width: | Height: | Size: 646 KiB |
BIN
src/dice/gemStone/normal.jpg
Normal file
After Width: | Height: | Size: 263 KiB |
49
src/dice/glass/GlassDice.js
Normal file
@ -0,0 +1,49 @@
|
||||
import * as BABYLON from "babylonjs";
|
||||
|
||||
import Dice from "../Dice";
|
||||
|
||||
import albedo from "./albedo.png";
|
||||
import mask from "./mask.png";
|
||||
import normal from "./normal.jpg";
|
||||
|
||||
class GlassDice extends Dice {
|
||||
static meshes;
|
||||
static material;
|
||||
|
||||
static loadMaterial(materialName, textures, scene) {
|
||||
let pbr = new BABYLON.PBRMaterial(materialName, scene);
|
||||
pbr.albedoTexture = new BABYLON.Texture(textures.albedo);
|
||||
pbr.normalTexture = new BABYLON.Texture(textures.normal);
|
||||
|
||||
pbr.roughness = 0.25;
|
||||
pbr.metallic = 0;
|
||||
|
||||
pbr.subSurface.isRefractionEnabled = true;
|
||||
pbr.subSurface.indexOfRefraction = 1.0;
|
||||
pbr.subSurface.isTranslucencyEnabled = true;
|
||||
pbr.subSurface.translucencyIntensity = 1.0;
|
||||
pbr.subSurface.minimumThickness = 10;
|
||||
pbr.subSurface.maximumThickness = 10;
|
||||
pbr.subSurface.tintColor = new BABYLON.Color3(43 / 255, 1, 115 / 255);
|
||||
pbr.subSurface.thicknessTexture = new BABYLON.Texture(textures.mask);
|
||||
pbr.subSurface.useMaskFromThicknessTexture = true;
|
||||
|
||||
return pbr;
|
||||
}
|
||||
|
||||
static async createInstance(diceType, scene) {
|
||||
if (!this.material) {
|
||||
this.material = this.loadMaterial(
|
||||
"glass_pbr",
|
||||
{ albedo, mask, normal },
|
||||
scene
|
||||
);
|
||||
}
|
||||
if (!this.meshes) {
|
||||
this.meshes = await this.loadMeshes("round", this.material, scene);
|
||||
}
|
||||
return Dice.createInstance(this.meshes[diceType], scene);
|
||||
}
|
||||
}
|
||||
|
||||
export default GlassDice;
|
BIN
src/dice/glass/albedo.png
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
src/dice/glass/mask.png
Normal file
After Width: | Height: | Size: 174 KiB |
BIN
src/dice/glass/normal.jpg
Normal file
After Width: | Height: | Size: 257 KiB |
BIN
src/dice/meshes/d20Round.glb
Normal file
BIN
src/dice/meshes/d20Sharp.glb
Normal file
26
src/dice/metal/MetalDice.js
Normal file
@ -0,0 +1,26 @@
|
||||
import Dice from "../Dice";
|
||||
|
||||
import albedo from "./albedo.jpg";
|
||||
import metalRoughness from "./metalRoughness.jpg";
|
||||
import normal from "./normal.jpg";
|
||||
|
||||
class MetalDice extends Dice {
|
||||
static meshes;
|
||||
static material;
|
||||
|
||||
static async createInstance(diceType, scene) {
|
||||
if (!this.material) {
|
||||
this.material = this.loadMaterial(
|
||||
"metal_pbr",
|
||||
{ albedo, metalRoughness, normal },
|
||||
scene
|
||||
);
|
||||
}
|
||||
if (!this.meshes) {
|
||||
this.meshes = await this.loadMeshes("round", this.material, scene);
|
||||
}
|
||||
return Dice.createInstance(this.meshes[diceType], scene);
|
||||
}
|
||||
}
|
||||
|
||||
export default MetalDice;
|
BIN
src/dice/metal/albedo.jpg
Normal file
After Width: | Height: | Size: 327 KiB |
BIN
src/dice/metal/metalRoughness.jpg
Normal file
After Width: | Height: | Size: 418 KiB |
BIN
src/dice/metal/normal.jpg
Normal file
After Width: | Height: | Size: 259 KiB |
27
src/dice/metalStone/MetalStoneDice.js
Normal file
@ -0,0 +1,27 @@
|
||||
import Dice from "../Dice";
|
||||
|
||||
import albedo from "./albedo.jpg";
|
||||
import metalRoughness from "./metalRoughness.jpg";
|
||||
import normal from "./normal.jpg";
|
||||
|
||||
class MetalStoneDice extends Dice {
|
||||
static meshes;
|
||||
static material;
|
||||
|
||||
static async createInstance(diceType, scene) {
|
||||
if (!this.material) {
|
||||
this.material = this.loadMaterial(
|
||||
"metal_stone_pbr",
|
||||
{ albedo, metalRoughness, normal },
|
||||
scene
|
||||
);
|
||||
}
|
||||
if (!this.meshes) {
|
||||
this.meshes = await this.loadMeshes("round", this.material, scene);
|
||||
}
|
||||
|
||||
return Dice.createInstance(this.meshes[diceType], scene);
|
||||
}
|
||||
}
|
||||
|
||||
export default MetalStoneDice;
|
BIN
src/dice/metalStone/albedo.jpg
Normal file
After Width: | Height: | Size: 943 KiB |
BIN
src/dice/metalStone/metalRoughness.jpg
Normal file
After Width: | Height: | Size: 776 KiB |
BIN
src/dice/metalStone/normal.jpg
Normal file
After Width: | Height: | Size: 261 KiB |
27
src/dice/wood/WoodDice.js
Normal file
@ -0,0 +1,27 @@
|
||||
import Dice from "../Dice";
|
||||
|
||||
import albedo from "./albedo.jpg";
|
||||
import metalRoughness from "./metalRoughness.jpg";
|
||||
import normal from "./normal.jpg";
|
||||
|
||||
class WoodDice extends Dice {
|
||||
static meshes;
|
||||
static material;
|
||||
|
||||
static async createInstance(diceType, scene) {
|
||||
if (!this.material) {
|
||||
this.material = this.loadMaterial(
|
||||
"wood_pbr",
|
||||
{ albedo, metalRoughness, normal },
|
||||
scene
|
||||
);
|
||||
}
|
||||
if (!this.meshes) {
|
||||
this.meshes = await this.loadMeshes("sharp", this.material, scene);
|
||||
}
|
||||
|
||||
return Dice.createInstance(this.meshes[diceType], scene);
|
||||
}
|
||||
}
|
||||
|
||||
export default WoodDice;
|
BIN
src/dice/wood/albedo.jpg
Normal file
After Width: | Height: | Size: 911 KiB |
BIN
src/dice/wood/metalRoughness.jpg
Normal file
After Width: | Height: | Size: 288 KiB |
BIN
src/dice/wood/normal.jpg
Normal file
After Width: | Height: | Size: 342 KiB |
22
yarn.lock
@ -2178,6 +2178,10 @@ alphanum-sort@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3"
|
||||
integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=
|
||||
|
||||
ammo.js@kripken/ammo.js#aab297a4164779c3a9d8dc8d9da26958de3cb778:
|
||||
version "0.0.2"
|
||||
resolved "https://codeload.github.com/kripken/ammo.js/tar.gz/aab297a4164779c3a9d8dc8d9da26958de3cb778"
|
||||
|
||||
ansi-colors@^3.0.0:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf"
|
||||
@ -2642,6 +2646,24 @@ babylon@^6.18.0:
|
||||
resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3"
|
||||
integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==
|
||||
|
||||
babylonjs-gltf2interface@4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/babylonjs-gltf2interface/-/babylonjs-gltf2interface-4.1.0.tgz#95ec994e352ac5cb74869e238218a1b4df18e2f4"
|
||||
integrity sha512-H2obg+4t8bcmLyzGiOQqmUaTQqTu+6mJUlsMWZvmRBf0k2fQVeTdAkH7aDy6HVIz/THvpIx4ntG1Lsyquvmc5Q==
|
||||
|
||||
babylonjs-loaders@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/babylonjs-loaders/-/babylonjs-loaders-4.1.0.tgz#b056423b98c1e3a3962491ec72dce5e0b8852295"
|
||||
integrity sha512-gNC+XEVI5cLJLVRTlkFHVfSY4EZS0VzWzEmNb8M49ZMFNuqOuHsVnQZg4Vms9e4LgvNtws4Z0SWrRanZnkIX5g==
|
||||
dependencies:
|
||||
babylonjs "4.1.0"
|
||||
babylonjs-gltf2interface "4.1.0"
|
||||
|
||||
babylonjs@4.1.0, babylonjs@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/babylonjs/-/babylonjs-4.1.0.tgz#a2d1d6765795e9d44f002831554d63d6275394bd"
|
||||
integrity sha512-MnaH1BQIL+PYgqGaAvGVdP8yd7nM1j6sbQi/K/6+RlkHPxIETW2NbjqxiW7Sywgy7r3PwqWT/gxG4Bz95Z6/yA==
|
||||
|
||||
backo2@1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947"
|
||||
|