| import * as BABYLON from "@babylonjs/core"; |
| import "@babylonjs/loaders"; |
| import Ammo from "ammojs-typed"; |
| |
| export function runTest(frames = 10) { |
| const classNames = Object.keys(BABYLON); |
| BABYLON.Logger.LogLevels = BABYLON.Logger.NoneLogLevel; |
| const engine = new BABYLON.NullEngine(); |
| const scene = createScene(engine); |
| for (let i = 0; i < frames; i++) { |
| scene.render(); |
| } |
| return { |
| classNames, |
| cameraRotationLength: scene.cameras[0].rotation.length(), |
| }; |
| } |
| |
| function createScene(engine) { |
| const scene = new BABYLON.Scene(engine); |
| scene.useConstantAnimationDeltaTime = true; |
| const camera = new BABYLON.FreeCamera( |
| "camera1", |
| new BABYLON.Vector3(0, 5, -10), |
| scene |
| ); |
| camera.setTarget(BABYLON.Vector3.Zero()); |
| const light = new BABYLON.HemisphericLight( |
| "light1", |
| new BABYLON.Vector3(0, 1, 0), |
| scene |
| ); |
| const sphere = BABYLON.MeshBuilder.CreateSphere( |
| "sphere1", |
| { segments: 16, diameter: 2, sideOrientation: BABYLON.Mesh.FRONTSIDE }, |
| scene |
| ); |
| sphere.position.y = 1; |
| const ground = BABYLON.MeshBuilder.CreateGround( |
| "ground1", |
| { width: 6, height: 6, subdivisions: 2, updatable: false }, |
| scene |
| ); |
| return scene; |
| } |
| |
| export async function runComplexScene( |
| fortData, |
| cannonData, |
| particleData, |
| frames = 10 |
| ) { |
| const classNames = Object.keys(BABYLON); |
| BABYLON.Logger.LogLevels = BABYLON.Logger.NoneLogLevel; |
| const engine = new BABYLON.NullEngine({ |
| deterministicLockstep: true, |
| }); |
| const scene = await createComplexScene( |
| engine, |
| fortData, |
| cannonData, |
| particleData |
| ); |
| for (let i = 0; i < frames; i++) { |
| scene.animate(); |
| scene.render(); |
| } |
| // Leak state to the outside. |
| return { |
| classNames, |
| cameraRotationLength: scene.cameras[0].rotation.length(), |
| }; |
| } |
| |
| async function createComplexScene(engine, fortData, cannonData, particleData) { |
| // Source: |
| // - https://www.babylonjs.com/featureDemos/ |
| // - https://playground.babylonjs.com/#C21DGD#2 |
| const scene = new BABYLON.Scene(engine); |
| scene.useConstantAnimationDeltaTime = true; |
| scene.clearColor = new BABYLON.Color3(0.31, 0.48, 0.64); |
| let camera = new BABYLON.ArcRotateCamera( |
| "camera", |
| BABYLON.Tools.ToRadians(125), |
| BABYLON.Tools.ToRadians(70), |
| 25, |
| new BABYLON.Vector3(0, 3, 0), |
| scene |
| ); |
| camera.lowerRadiusLimit = 10; |
| const ammo = await Ammo(); |
| scene.enablePhysics( |
| new BABYLON.Vector3(0, -9.8, 0), |
| new BABYLON.AmmoJSPlugin(true, ammo) |
| ); |
| let cannonAnimationPairings = {}; |
| let cannonReadyToPlay = {}; |
| |
| //create a cannonBall template to clone from, set it's visibility to off. |
| let cannonBall = BABYLON.MeshBuilder.CreateSphere( |
| "cannonBall", |
| { diameter: 0.3 }, |
| scene |
| ); |
| let cannonBallMat = new BABYLON.StandardMaterial("cannonBallMaterial", scene); |
| cannonBallMat.diffuseColor = BABYLON.Color3.Black(); |
| cannonBallMat.specularPower = 256; |
| cannonBall.material = cannonBallMat; |
| cannonBall.visibility = false; |
| |
| //create a large box far underneath the tower, that will act as a trigger to destroy the cannonballs. |
| let killBox = BABYLON.MeshBuilder.CreateBox( |
| "killBox", |
| { width: 400, depth: 400, height: 4 }, |
| scene |
| ); |
| killBox.position = new BABYLON.Vector3(0, -50, 0); |
| killBox.visibility = 0; |
| |
| const pirateFortImport = await BABYLON.ImportMeshAsync(fortData, scene, { |
| pluginExtension: ".glb", |
| }); |
| pirateFortImport.meshes[0].name = "pirateFort"; |
| scene.getMeshByName("sea").material.needDepthPrePass = true; |
| scene.getLightByName("Sun").intensity = 12; |
| |
| //Load the cannon model and create clones |
| const cannonImportResult = await BABYLON.ImportMeshAsync(cannonData, scene, { |
| pluginExtension: ".glb", |
| }); |
| //remove the top level root node |
| let cannon = cannonImportResult.meshes[0].getChildren()[0]; |
| cannon.setParent(null); |
| cannonImportResult.meshes[0].dispose(); |
| |
| //set the metadata of each mesh to filter on later |
| let cannonMeshes = cannon.getChildMeshes(); |
| for (let i = 0; i < cannonMeshes.length; i++) { |
| cannonMeshes[i].metadata = "cannon"; |
| } |
| |
| const importedAnimGroups = cannonImportResult.animationGroups; |
| |
| //loop through all imported animation groups and copy the animation curve data to an array. |
| let animations = []; |
| for (let i = 0; i < importedAnimGroups.length; i++) { |
| importedAnimGroups[i].stop(); |
| animations.push(importedAnimGroups[i].targetedAnimations[0].animation); |
| importedAnimGroups[i].dispose(); |
| } |
| |
| //create a new animation group and add targeted animations based on copied curve data from the "animations" array. |
| let cannonAnimGroup = new BABYLON.AnimationGroup("cannonAnimGroup"); |
| cannonAnimGroup.addTargetedAnimation( |
| animations[0], |
| cannon.getChildMeshes()[1] |
| ); |
| cannonAnimGroup.addTargetedAnimation( |
| animations[1], |
| cannon.getChildMeshes()[0] |
| ); |
| |
| //create a box for particle emission, position it at the muzzle of the cannon, turn off visibility and parent it to the cannon mesh |
| let particleEmitter = BABYLON.MeshBuilder.CreateBox( |
| "particleEmitter", |
| { size: 0.05 }, |
| scene |
| ); |
| particleEmitter.position = new BABYLON.Vector3(0, 0.76, 1.05); |
| particleEmitter.rotation.x = BABYLON.Tools.ToRadians(78.5); |
| particleEmitter.isVisible = false; |
| particleEmitter.setParent(cannon.getChildMeshes()[1]); |
| |
| //load particle system from the snippet server and set the emitter to the particleEmitter. Set its stopDuration. |
| const smokeBlast = BABYLON.ParticleSystem.Parse(particleData, scene, ""); |
| smokeBlast.emitter = particleEmitter; |
| smokeBlast.targetStopDuration = 0.2; |
| |
| //position and rotation data for the placement of the cannon clones |
| let cannonPositionArray = [ |
| [ |
| new BABYLON.Vector3(0.97, 5.52, 1.79), |
| new BABYLON.Vector3( |
| BABYLON.Tools.ToRadians(0), |
| BABYLON.Tools.ToRadians(0), |
| BABYLON.Tools.ToRadians(180) |
| ), |
| ], |
| [ |
| new BABYLON.Vector3(1.08, 2.32, 3.05), |
| new BABYLON.Vector3( |
| BABYLON.Tools.ToRadians(0), |
| BABYLON.Tools.ToRadians(0), |
| BABYLON.Tools.ToRadians(180) |
| ), |
| ], |
| [ |
| new BABYLON.Vector3(1.46, 2.35, -0.73), |
| new BABYLON.Vector3( |
| BABYLON.Tools.ToRadians(0), |
| BABYLON.Tools.ToRadians(90), |
| BABYLON.Tools.ToRadians(180) |
| ), |
| ], |
| [ |
| new BABYLON.Vector3(1.45, 5.52, -1.66), |
| new BABYLON.Vector3( |
| BABYLON.Tools.ToRadians(0), |
| BABYLON.Tools.ToRadians(90), |
| BABYLON.Tools.ToRadians(180) |
| ), |
| ], |
| [ |
| new BABYLON.Vector3(1.49, 8.69, -0.35), |
| new BABYLON.Vector3( |
| BABYLON.Tools.ToRadians(0), |
| BABYLON.Tools.ToRadians(90), |
| BABYLON.Tools.ToRadians(180) |
| ), |
| ], |
| [ |
| new BABYLON.Vector3(-1.37, 8.69, -0.39), |
| new BABYLON.Vector3( |
| BABYLON.Tools.ToRadians(0), |
| BABYLON.Tools.ToRadians(-90), |
| BABYLON.Tools.ToRadians(180) |
| ), |
| ], |
| [ |
| new BABYLON.Vector3(0.58, 4, -2.18), |
| new BABYLON.Vector3( |
| BABYLON.Tools.ToRadians(0), |
| BABYLON.Tools.ToRadians(180), |
| BABYLON.Tools.ToRadians(180) |
| ), |
| ], |
| [ |
| new BABYLON.Vector3(1.22, 8.69, -2.5), |
| new BABYLON.Vector3( |
| BABYLON.Tools.ToRadians(0), |
| BABYLON.Tools.ToRadians(180), |
| BABYLON.Tools.ToRadians(180) |
| ), |
| ], |
| [ |
| new BABYLON.Vector3(-1.31, 2.33, -2.45), |
| new BABYLON.Vector3( |
| BABYLON.Tools.ToRadians(0), |
| BABYLON.Tools.ToRadians(180), |
| BABYLON.Tools.ToRadians(180) |
| ), |
| ], |
| [ |
| new BABYLON.Vector3(-3.54, 5.26, -2.12), |
| new BABYLON.Vector3( |
| BABYLON.Tools.ToRadians(0), |
| BABYLON.Tools.ToRadians(-90), |
| BABYLON.Tools.ToRadians(180) |
| ), |
| ], |
| ]; |
| |
| //create 10 cannon clones, each with unique position/rotation data. Note that particle systems are cloned with parent meshes |
| //also create 10 new animation groups with targeted animations applied to the newly cloned meshes |
| for (let i = 0; i < 10; i++) { |
| let cannonClone = cannon.clone("cannonClone" + i); |
| cannonClone.position = cannonPositionArray[i][0]; |
| cannonClone.rotation = cannonPositionArray[i][1]; |
| let cannonAnimGroupClone = new BABYLON.AnimationGroup( |
| "cannonAnimGroupClone" + i |
| ); |
| cannonAnimGroupClone.addTargetedAnimation( |
| cannonAnimGroup.targetedAnimations[0].animation, |
| cannonClone.getChildMeshes()[1] |
| ); |
| cannonAnimGroupClone.addTargetedAnimation( |
| cannonAnimGroup.targetedAnimations[1].animation, |
| cannonClone.getChildMeshes()[0] |
| ); |
| |
| //store a key/value pair of each clone name and the name of the associated animation group name. |
| cannonAnimationPairings[cannonClone.name] = cannonAnimGroupClone.name; |
| //store key/value pair for the cannon name and it's readyToPlay status as 1; |
| cannonReadyToPlay[cannonClone.name] = 1; |
| } |
| |
| //dispose of the original cannon, animation group, and particle system |
| cannon.dispose(); |
| cannonAnimGroup.dispose(); |
| smokeBlast.dispose(); |
| |
| return scene; |
| } |