diff --git a/package.json b/package.json index 341f414..d5b1828 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/components/map/Map.js b/src/components/map/Map.js index 965afef..2cd5170 100644 --- a/src/components/map/Map.js +++ b/src/components/map/Map.js @@ -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={ + <> + + {mapControls} + {loading && } + + } > {map && mapImage} {map && mapDrawing} diff --git a/src/components/map/MapDice.js b/src/components/map/MapDice.js new file mode 100644 index 0000000..bbcbba3 --- /dev/null +++ b/src/components/map/MapDice.js @@ -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 ( + + +
+ {dieNumbers.map((num, index) => ( +

+ {num || "?"} + {index === dieNumbers.length - 1 ? "" : "+"} +

+ ))} +

+ {dieNumbers.length > 0 && + `= ${dieNumbers.reduce((a, b) => (a || 0) + (b || 0))}`} +

+
+
+ + + + + + +
+
+ ); +} + +export default MapDice; diff --git a/src/components/map/MapDiceScene.js b/src/components/map/MapDiceScene.js new file mode 100644 index 0000000..07c8780 --- /dev/null +++ b/src/components/map/MapDiceScene.js @@ -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 ( +
+ +
+ ); +} + +export default MapDiceScene; diff --git a/src/components/map/MapInteraction.js b/src/components/map/MapInteraction.js index 06e0cd0..af60f34 100644 --- a/src/components/map/MapInteraction.js +++ b/src/components/map/MapInteraction.js @@ -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({ - {controls} - {loading && } + {sideContent} ); } diff --git a/src/dice/Dice.js b/src/dice/Dice.js new file mode 100644 index 0000000..b953ccc --- /dev/null +++ b/src/dice/Dice.js @@ -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; diff --git a/src/dice/color/ColorDice.js b/src/dice/color/ColorDice.js new file mode 100644 index 0000000..672355b --- /dev/null +++ b/src/dice/color/ColorDice.js @@ -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; diff --git a/src/dice/color/albedo.jpg b/src/dice/color/albedo.jpg new file mode 100644 index 0000000..f26d42a Binary files /dev/null and b/src/dice/color/albedo.jpg differ diff --git a/src/dice/color/metalRoughness.jpg b/src/dice/color/metalRoughness.jpg new file mode 100644 index 0000000..b749395 Binary files /dev/null and b/src/dice/color/metalRoughness.jpg differ diff --git a/src/dice/color/normal.jpg b/src/dice/color/normal.jpg new file mode 100644 index 0000000..0f57fc7 Binary files /dev/null and b/src/dice/color/normal.jpg differ diff --git a/src/dice/environment.dds b/src/dice/environment.dds new file mode 100755 index 0000000..e8c89af Binary files /dev/null and b/src/dice/environment.dds differ diff --git a/src/dice/gemStone/GemStoneDice.js b/src/dice/gemStone/GemStoneDice.js new file mode 100644 index 0000000..f7c6993 --- /dev/null +++ b/src/dice/gemStone/GemStoneDice.js @@ -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; diff --git a/src/dice/gemStone/albedo.jpg b/src/dice/gemStone/albedo.jpg new file mode 100644 index 0000000..dfd2132 Binary files /dev/null and b/src/dice/gemStone/albedo.jpg differ diff --git a/src/dice/gemStone/metalRoughness.jpg b/src/dice/gemStone/metalRoughness.jpg new file mode 100644 index 0000000..7512e39 Binary files /dev/null and b/src/dice/gemStone/metalRoughness.jpg differ diff --git a/src/dice/gemStone/normal.jpg b/src/dice/gemStone/normal.jpg new file mode 100644 index 0000000..1290434 Binary files /dev/null and b/src/dice/gemStone/normal.jpg differ diff --git a/src/dice/glass/GlassDice.js b/src/dice/glass/GlassDice.js new file mode 100644 index 0000000..961b9be --- /dev/null +++ b/src/dice/glass/GlassDice.js @@ -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; diff --git a/src/dice/glass/albedo.png b/src/dice/glass/albedo.png new file mode 100644 index 0000000..50c48bd Binary files /dev/null and b/src/dice/glass/albedo.png differ diff --git a/src/dice/glass/mask.png b/src/dice/glass/mask.png new file mode 100644 index 0000000..27eb660 Binary files /dev/null and b/src/dice/glass/mask.png differ diff --git a/src/dice/glass/normal.jpg b/src/dice/glass/normal.jpg new file mode 100644 index 0000000..5f0b6d6 Binary files /dev/null and b/src/dice/glass/normal.jpg differ diff --git a/src/dice/meshes/d20Round.glb b/src/dice/meshes/d20Round.glb new file mode 100644 index 0000000..5451233 Binary files /dev/null and b/src/dice/meshes/d20Round.glb differ diff --git a/src/dice/meshes/d20Sharp.glb b/src/dice/meshes/d20Sharp.glb new file mode 100644 index 0000000..0749d5b Binary files /dev/null and b/src/dice/meshes/d20Sharp.glb differ diff --git a/src/dice/metal/MetalDice.js b/src/dice/metal/MetalDice.js new file mode 100644 index 0000000..7613d89 --- /dev/null +++ b/src/dice/metal/MetalDice.js @@ -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; diff --git a/src/dice/metal/albedo.jpg b/src/dice/metal/albedo.jpg new file mode 100644 index 0000000..57b6df4 Binary files /dev/null and b/src/dice/metal/albedo.jpg differ diff --git a/src/dice/metal/metalRoughness.jpg b/src/dice/metal/metalRoughness.jpg new file mode 100644 index 0000000..5fddf0d Binary files /dev/null and b/src/dice/metal/metalRoughness.jpg differ diff --git a/src/dice/metal/normal.jpg b/src/dice/metal/normal.jpg new file mode 100644 index 0000000..1be1284 Binary files /dev/null and b/src/dice/metal/normal.jpg differ diff --git a/src/dice/metalStone/MetalStoneDice.js b/src/dice/metalStone/MetalStoneDice.js new file mode 100644 index 0000000..5662a5d --- /dev/null +++ b/src/dice/metalStone/MetalStoneDice.js @@ -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; diff --git a/src/dice/metalStone/albedo.jpg b/src/dice/metalStone/albedo.jpg new file mode 100644 index 0000000..62e3ea2 Binary files /dev/null and b/src/dice/metalStone/albedo.jpg differ diff --git a/src/dice/metalStone/metalRoughness.jpg b/src/dice/metalStone/metalRoughness.jpg new file mode 100644 index 0000000..eb10923 Binary files /dev/null and b/src/dice/metalStone/metalRoughness.jpg differ diff --git a/src/dice/metalStone/normal.jpg b/src/dice/metalStone/normal.jpg new file mode 100644 index 0000000..821e245 Binary files /dev/null and b/src/dice/metalStone/normal.jpg differ diff --git a/src/dice/wood/WoodDice.js b/src/dice/wood/WoodDice.js new file mode 100644 index 0000000..6a26ed0 --- /dev/null +++ b/src/dice/wood/WoodDice.js @@ -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; diff --git a/src/dice/wood/albedo.jpg b/src/dice/wood/albedo.jpg new file mode 100644 index 0000000..8df9e7d Binary files /dev/null and b/src/dice/wood/albedo.jpg differ diff --git a/src/dice/wood/metalRoughness.jpg b/src/dice/wood/metalRoughness.jpg new file mode 100644 index 0000000..eee1bda Binary files /dev/null and b/src/dice/wood/metalRoughness.jpg differ diff --git a/src/dice/wood/normal.jpg b/src/dice/wood/normal.jpg new file mode 100644 index 0000000..a236336 Binary files /dev/null and b/src/dice/wood/normal.jpg differ diff --git a/yarn.lock b/yarn.lock index 9cb4b13..2db3f6f 100644 --- a/yarn.lock +++ b/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"