Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <html>
- <!DOCTYPE html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
- <title>Babylon.js sample code</title>
- <!-- Babylon.js -->
- <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
- <script src="https://preview.babylonjs.com/ammo.js"></script>
- <script src="https://preview.babylonjs.com/cannon.js"></script>
- <script src="https://preview.babylonjs.com/Oimo.js"></script>
- <script src="https://preview.babylonjs.com/earcut.min.js"></script>
- <script src="https://preview.babylonjs.com/babylon.max.js"></script>
- <script src="https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.js"></script>
- <script src="https://preview.babylonjs.com/proceduralTexturesLibrary/babylonjs.proceduralTextures.js"></script>
- <script src="https://preview.babylonjs.com/postProcessesLibrary/babylonjs.postProcess.js"></script>
- <script src="https://preview.babylonjs.com/loaders/babylonjs.loaders.js"></script>
- <script src="https://preview.babylonjs.com/serializers/babylonjs.serializers.js"></script>
- <script src="https://preview.babylonjs.com/gui/babylon.gui.js"></script>
- <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>
- <style>
- html,
- body {
- overflow: hidden;
- width: 100%;
- height: 100%;
- margin: 0;
- padding: 0;
- }
- #renderCanvas {
- width: 100%;
- height: 100%;
- touch-action: none;
- }
- </style>
- </head>
- <body>
- <canvas id="renderCanvas"></canvas>
- <canvas id="computeCanvas"></canvas>
- <textarea id="calculate">
- [[block]] struct SimParams {
- time: f32;
- count: f32;
- };
- [[binding(0), group(0)]] var<uniform> params : SimParams;
- struct Object
- {
- position: vec3<f32>;
- collisionMark: f32;
- velocity: vec3<f32>;
- scaleAfterCollision: f32;
- force: vec3<f32>;
- disabled: f32;
- mass: f32;
- size: f32;
- id: f32;
- dummy5: f32;
- };
- [[block]] struct Objects {
- data : /*[[stride(64)]]*/ array<Object>;
- };
- [[binding(1), group(0)]] var<storage, read> ssboIn : Objects;
- [[binding(2), group(0)]] var<storage, read_write> ssboOut : Objects;
- [[stage(compute), workgroup_size(WORK_GROUP_SIZE)]]
- fn main([[builtin(global_invocation_id)]] GlobalInvocationID : vec3<u32>)
- {
- var threadIndex = GlobalInvocationID.x; // gl_WorkGroupID * gl_WorkGroupSize.x + gl_LocalInvocationID.x
- var G = 6.67408313131E-11;
- var time = params.time;
- var count = u32(params.count);
- var first = ssboIn.data[threadIndex];
- if (first.disabled > 0.0)
- {
- // no calculation required
- return;
- }
- var m1 = first.mass;
- var forceVector = vec3<f32>(0.0, 0.0, 0.0);
- // calculate result force on all objects/planets
- for (var j : u32 = 0u; j < count; j=j+1u)
- {
- if (threadIndex == j)
- {
- continue;
- }
- var second = ssboIn.data[j];
- var m2 = second.mass;
- if (second.disabled > 0.0)
- {
- // skip no mass object
- continue;
- }
- var dist = distance(first.position, second.position);
- var distanceSquared = dist * dist;
- if (distanceSquared > 0.0)
- {
- var force = G * m1 * m2 / distanceSquared;
- var directionVector = normalize((second.position - first.position));
- forceVector = forceVector + (directionVector * force);
- }
- }
- // calculate new position depends on force
- var a = forceVector / m1;
- var v = a * time;
- var s = v * time / 2.0;
- var newPosition = first.position + (first.velocity * time) + s;
- ssboOut.data[threadIndex].force = forceVector;
- ssboOut.data[threadIndex].mass = m1;
- ssboOut.data[threadIndex].size = first.size;
- ssboOut.data[threadIndex].position = newPosition;
- ssboOut.data[threadIndex].velocity = first.velocity + v;
- ssboOut.data[threadIndex].scaleAfterCollision = 0.0;
- ssboOut.data[threadIndex].dummy5 = first.dummy5 + time;
- // calculate collisions
- for(var j : u32 = 0u; j < count; j=j+1u)
- {
- if (threadIndex == j)
- {
- continue;
- }
- var second = ssboIn.data[j];
- var m2 = second.mass;
- if (second.disabled > 0.0)
- {
- // skip no mass object
- continue;
- }
- // calculate collision
- var a = distance(newPosition, first.position);
- if (a > 0.0)
- {
- var b = distance(first.position, second.position);
- var c = distance(newPosition, second.position);
- var radius = (first.size + second.size) / 2.0;
- var id1 = first.id;
- var id2 = second.id;
- if (b < radius || c < radius) {
- if (m1 > m2 || (m1 == m2 && id1 > id2)) {
- // fix speed v = (m1v1 + m2v2) / (m1 + m2)
- var v1 = first.velocity;
- var v2 = second.velocity;
- var vresult = (v1 * m1 + v2 * m2) / (m1 + m2);
- var newR = pow((first.size * first.size * first.size
- + second.size * second.size * second.size), 1.0 / 3.0);
- // mark collision
- ssboOut.data[threadIndex].collisionMark = 123.0;
- ssboOut.data[threadIndex].velocity = vresult;
- ssboOut.data[threadIndex].mass = m1 + m2;
- ssboOut.data[threadIndex].size = newR;
- ssboOut.data[threadIndex].scaleAfterCollision = newR / first.size;
- } else {
- // collision from other side
- ssboOut.data[threadIndex].collisionMark = 246.0;
- ssboOut.data[threadIndex].mass = 0.0;
- ssboOut.data[threadIndex].velocity = vec3<f32>(0.0, 0.0, 0.0);
- ssboOut.data[threadIndex].force = vec3<f32>(0.0, 0.0, 0.0);
- ssboOut.data[threadIndex].scaleAfterCollision = 0.0;
- ssboOut.data[threadIndex].disabled = 9.0;
- }
- // no more calculations after collision
- break;
- }
- }
- }
- }
- </textarea>
- <script>
- var debug = false;
- var worldScale = 1;
- var worldToMetres = 1 / worldScale;
- //var scope = 1600.0 * worldScale;
- var scope = 50.0 * worldScale;
- var sunZoom = 1.0;
- var planetsZoom = 1.0;
- var forceZoom = 1.5 * Math.pow(10, -5);
- var velocityZoom = 1 * Math.pow(10, 1);
- //var iters_per_step = 60 * 60;
- var iters_per_step = 1;
- var t = 1; // 1 second
- //var t = 0.01; // 1 second
- var G = 6.67408313131 * Math.pow(10, -11);
- class Runner {
- constructor() {
- // const
- this.WORK_GROUP_SIZE = 256;
- this.dataSize = 3 + 3 + 3 + 1 + 1 + 5 /*align*/;
- this.numInstances = this.WORK_GROUP_SIZE * 10;
- //this.numInstances = 2;
- this.range = 500;
- this.numGroups = Math.ceil(this.numInstances / this.WORK_GROUP_SIZE);
- this.canvas = document.getElementById("renderCanvas");
- this.computeCanvas = document.getElementById("computeCanvas");
- this.planets = [];
- this.planetInitialObjects = [];
- this.initInitialData();
- }
- async init() {
- const engine = this.engine = new BABYLON.WebGPUEngine(this.canvas);
- await engine.initAsync();
- }
- async initCompute() {
- const supportCS = this.engine.getCaps().supportComputeShaders;
- if (!supportCS) {
- return true;
- }
- const computeShaderSource = document.getElementById("calculate").value.replace('WORK_GROUP_SIZE', this.WORK_GROUP_SIZE);
- this.computeShader = new BABYLON.ComputeShader("compute", this.engine, { computeSource: computeShaderSource }, {
- bindingsMapping:
- {
- "params": { group: 0, binding: 0 },
- "ssboIn": { group: 0, binding: 1 },
- "ssboOut": { group: 0, binding: 2 },
- }
- });
- const simParamsBuffer = new BABYLON.UniformBuffer(this.engine);
- simParamsBuffer.updateFloat2("time", t, this.numInstances);
- simParamsBuffer.update();
- const ssboInBuffer = new BABYLON.StorageBuffer(this.engine, this.ssboData.byteLength);
- ssboInBuffer.update(this.ssboData);
- const ssboOutBuffer = new BABYLON.StorageBuffer(this.engine, this.ssboData.byteLength);
- this.computeShader.setUniformBuffer("params", simParamsBuffer);
- this.computeShader.setStorageBuffer("ssboIn", ssboInBuffer);
- this.computeShader.setStorageBuffer("ssboOut", ssboOutBuffer);
- const handler = () => {
- ssboOutBuffer.read().then((res) => {
- const resFloats = new Float32Array(res.buffer);
- //console.log(resFloats);
- this.ssboData.set(resFloats);
- ssboInBuffer.update(this.ssboData);
- this.computeShader.setStorageBuffer("ssboIn", ssboInBuffer);
- this.computeShader.dispatchWhenReady(this.numGroups).then(handler);
- this.loadPositions();
- });
- };
- this.computeShader.dispatchWhenReady(this.numGroups).then(handler);
- }
- initInitialData() {
- // ssbo Data
- const ssboData = this.ssboData = new Float32Array(this.numInstances * this.dataSize);
- this.initPlanets(ssboData);
- }
- initPlanets(ssboData) {
- const count = ssboData.length / this.dataSize;
- for (let i = 0; i < count; i++) {
- const sizeObj = 5.0 * Math.random();
- const range = this.range;
- const obj = {
- name: "pl" + i,
- mass: sizeObj * 100000000, // if 2 objects collide with the same mass, it causes issue to calculation as we do not know from which side to make calculation about collision
- perihelion: range * (Math.random() - 0.5),
- aphelion: range * (Math.random() - 0.5),
- Z: range * (Math.random() - 0.5),
- size: sizeObj,
- velocity: 0,
- zoom: planetsZoom
- };
- this.planetInitialObjects.push(obj);
- const force = new BABYLON.Vector3(0, 0, 0);
- const position = new BABYLON.Vector3(obj.perihelion, obj.aphelion, obj.Z);
- const velocity = new BABYLON.Vector3(0, obj.velocity, 0);
- const mass = obj.mass;
- const size = obj.size;
- // store into buffer
- // --------------------
- // vec3 position;
- // vec3 velocity;
- // vec3 force;
- // float mass;
- // float size;
- let bi = i * this.dataSize;
- ssboData[bi + 0] = position.x;
- ssboData[bi + 1] = position.y;
- ssboData[bi + 2] = position.z;
- ssboData[bi + 3] = 0;
- ssboData[bi + 4] = velocity.x;
- ssboData[bi + 5] = velocity.y;
- ssboData[bi + 6] = velocity.z;
- ssboData[bi + 7] = 0;
- ssboData[bi + 8] = force.x;
- ssboData[bi + 9] = force.y;
- ssboData[bi + 10] = force.z;
- ssboData[bi + 11] = 0;
- ssboData[bi + 12] = mass;
- ssboData[bi + 13] = size;
- ssboData[bi + 14] = i; // id
- ssboData[bi + 15] = 0;
- }
- }
- loadPositions() {
- if (!this.ssboData || !this.ssboData.length || !this.planets || !this.planets.length) {
- return;
- }
- let enabledCount = 0;
- const count = this.ssboData.length / this.dataSize;
- for (let i = 0; i < count; i++) {
- let bi = i * this.dataSize;
- const planet = this.planets[i];
- if (this.ssboData[bi + 11] > 0.0) {
- // mass is 0, disable shape
- planet.setEnabled(false);
- continue;
- }
- enabledCount++;
- planet.position.x = this.ssboData[bi + 0] / worldToMetres;
- planet.position.y = this.ssboData[bi + 2] / worldToMetres; // z
- planet.position.z = this.ssboData[bi + 1] / worldToMetres; // y
- if (debug) {
- if (!planet.linearVelocity) {
- planet.linearVelocity = new BABYLON.Vector3(0, 0, 0);
- }
- planet.linearVelocity.x = this.ssboData[bi + 4];
- planet.linearVelocity.y = this.ssboData[bi + 6]; // z
- planet.linearVelocity.z = this.ssboData[bi + 5]; // y
- if (!planet.forceVector) {
- planet.forceVector = new BABYLON.Vector3(0, 0, 0);
- }
- planet.forceVector.x = this.ssboData[bi + 8];
- planet.forceVector.y = this.ssboData[bi + 10]; // z
- planet.forceVector.z = this.ssboData[bi + 9]; // y
- }
- const collisionMark = this.ssboData[bi + 3];
- if (collisionMark == 123.0 || collisionMark == 246.0) {
- if (collisionMark == 123.0) {
- planet.material = this.materialCol;
- }
- const scale = this.ssboData[bi + 7];
- if (scale > 0.0) {
- if (planet.scaling == null) {
- planet.scaling = new BABYLON.Vector3(scale, scale, scale);
- } else {
- planet.scaling.scaleInPlace(scale);
- }
- }
- }
- }
- if (enabledCount < 10) {
- debug = true;
- }
- }
- createAxisLine(scene, line, i, color, first) {
- if (i == 0) {
- if (!first) {
- return;
- }
- var _mesh = BABYLON.Mesh.CreateLines("", line, scene, true);
- }
- else {
- var _mesh = BABYLON.Mesh.CreateDashedLines("", line, 0.7, 0.0, 0.0, scene, true);
- }
- _mesh.material.checkReadyOnlyOnce = true;
- _mesh.color = color;
- }
- createAxis(scene, scale, pstep) {
- var step = pstep || 1;
- // xy
- for (var i = -scale; i <= scale; i += step) {
- var _xline = [new BABYLON.Vector3(-scale, i, 0), new BABYLON.Vector3(scale, i, 0)];
- this.createAxisLine(scene, _xline, i, new BABYLON.Color3(1, 0, 0), true);
- var _yline = [new BABYLON.Vector3(i, -scale, 0), new BABYLON.Vector3(i, scale, 0)];
- this.createAxisLine(scene, _yline, i, new BABYLON.Color3(1, 0, 0), false);
- }
- // xz
- for (var i = -scale; i <= scale; i += step) {
- var _xline = [new BABYLON.Vector3(-scale, 0, i), new BABYLON.Vector3(scale, 0, i)];
- this.createAxisLine(scene, _xline, i, new BABYLON.Color3(0, 1, 0), false);
- var _zline = [new BABYLON.Vector3(i, 0, -scale), new BABYLON.Vector3(i, 0, scale)];
- this.createAxisLine(scene, _zline, i, new BABYLON.Color3(0, 1, 0), true);
- }
- // yz
- for (var i = -scale; i <= scale; i += step) {
- var _yline = [new BABYLON.Vector3(0, -scale, i), new BABYLON.Vector3(0, scale, i)];
- this.createAxisLine(scene, _yline, i, new BABYLON.Color3(0, 0, 1), true);
- var _zline = [new BABYLON.Vector3(0, i, -scale), new BABYLON.Vector3(0, i, scale)];
- this.createAxisLine(scene, _zline, i, new BABYLON.Color3(0, 0, 1), false);
- }
- }
- createPlanet(scene, material, params) {
- if (!params) {
- return;
- }
- var planet = BABYLON.Mesh.CreateSphere(params.name, 2, (params.size / worldToMetres) * params.zoom * worldScale, scene);
- planet.material = material;
- /*
- planet.position.x = params.perihelion || 0 / worldToMetres;
- planet.position.y = params.aphelion || 0 / worldToMetres;
- planet.position.z = 0;
- */
- planet.position.x = params.perihelion || 0 / worldToMetres;
- planet.position.y = params.Z || 0 / worldToMetres;
- planet.position.z = params.aphelion || 0 / worldToMetres;
- planet.forceDots = [new BABYLON.Vector3(0, 0, 0), new BABYLON.Vector3(0, 0, 0)];
- planet.forceLine = BABYLON.Mesh.CreateLines("", planet.forceDots, scene, true);
- planet.forceLine.color = new BABYLON.Color3(1, 1, 1);
- // velocity line
- planet.velocityDots = [new BABYLON.Vector3(0, 0, 0), new BABYLON.Vector3(0, 0, 0)];
- planet.velocityLine = BABYLON.Mesh.CreateLines("", planet.velocityDots, scene, true);
- planet.velocityLine.color = new BABYLON.Color3(0, 1, 1);
- // track
- planet.trackDots = [];
- planet.trackLine = null;
- this.planets.push(planet);
- }
- debugOutput(planet, scene) {
- var physPos = planet.position
- var forceVector = planet.forceVector;
- if (!forceVector) {
- return;
- }
- // draw velocity
- var newPosition = new BABYLON.Vector3(physPos.x, physPos.y, physPos.z);
- // draw force
- var forceDots = planet.forceDots;
- var forceLine = planet.forceLine;
- forceDots[0].copyFrom(newPosition);
- forceDots[1].copyFrom(forceVector.scale(forceZoom).add(newPosition));
- if (!planet.isEnabled()) {
- forceDots[0].x = 0;
- forceDots[0].y = 0;
- forceDots[0].z = 0;
- forceDots[1].x = 0;
- forceDots[1].y = 0;
- forceDots[1].z = 0;
- }
- BABYLON.Mesh.CreateLines("forceVector", forceDots, null, false, forceLine);
- if (!planet.isEnabled()) {
- planet.forceLine.setEnabled(false);
- }
- var velocityDots = planet.velocityDots;
- var velocityLine = planet.velocityLine;
- velocityDots[0].copyFrom(newPosition);
- velocityDots[1].copyFrom(planet.linearVelocity.scale(velocityZoom).add(newPosition));
- if (!planet.isEnabled()) {
- velocityDots[0].x = 0;
- velocityDots[0].y = 0;
- velocityDots[0].z = 0;
- velocityDots[1].x = 0;
- velocityDots[1].y = 0;
- velocityDots[1].z = 0;
- }
- BABYLON.Mesh.CreateLines("velocityVector", velocityDots, null, false, velocityLine);
- if (!planet.isEnabled()) {
- planet.velocityLine.setEnabled(false);
- }
- // track
- var trackDots = planet.trackDots;
- var trackLine = planet.trackLine;
- if (trackLine != null) {
- trackLine.dispose();
- }
- if (trackDots.length == 0 || trackDots[trackDots.length - 1].subtract(newPosition).length() > 5) {
- trackDots.push(newPosition);
- }
- if (trackDots.length > 1) {
- trackLine = BABYLON.Mesh.CreateLines("track", trackDots, scene, false);
- trackLine.color = this.trackColor;
- }
- }
- createScene() {
- // This creates a basic Babylon Scene object (non-mesh)
- var scene = new BABYLON.Scene(this.engine);
- //scene.enablePhysics(new BABYLON.Vector3(0, 0/*-9.8*/, 0));
- var distanceValueCtrl = document.getElementById("distanceValue");
- // This creates and positions a free camera (non-mesh)
- var camera = new BABYLON.ArcRotateCamera("ArcRotateCamera", 1, 0.8, scope, BABYLON.Vector3.Zero(), scene);
- camera.setPosition(new BABYLON.Vector3(0, scope * 2, scope * 2));
- // This attaches the camera to the canvas
- camera.attachControl(this.canvas, true);
- // important material
- //var material = new BABYLON.StandardMaterial("texture1", scene);
- var material = new BABYLON.ShaderMaterial("colorShader", scene, "color",
- {
- attributes: [BABYLON.VertexBuffer.PositionKind],
- uniforms: ["world", "viewProjection", "color"]
- });
- material.setColor4("color", new BABYLON.Color3(0.5, 0.6, 0.7).toColor4());
- ////materialPlane.wireframe = true;
- material.backFaceCulling = false;//Always show the front and the back of an element
- material.wireframe = true;//debug;
- var materialCol = new BABYLON.ShaderMaterial("colorShader", scene, "color",
- {
- attributes: [BABYLON.VertexBuffer.PositionKind],
- uniforms: ["world", "viewProjection", "color"]
- });
- materialCol.setColor4("color", new BABYLON.Color3(0.9, 0.1, 0.1).toColor4());
- ////materialPlane.wireframe = true;
- materialCol.backFaceCulling = false;//Always show the front and the back of an element
- materialCol.wireframe = true;//debug;
- this.materialCol = materialCol;
- this.trackColor = new BABYLON.Color3(0.5, 0.5, 1);
- this.createAxis(scene, scope, scope / 10.0);
- //this.createPlanet(scene, material, sun);
- //this.createPlanet(scene, material, earth);
- for (var i = 0; i < this.numInstances; i++) {
- this.createPlanet(scene, material, this.planetInitialObjects[i]);
- }
- return scene;
- }
- async run() {
- await this.init();
- await this.initCompute();
- var scene = this.createScene()
- this.engine.runRenderLoop(() => {
- if (debug) {
- for (var index = 0; index < this.planets.length; index++) {
- var planet = this.planets[index];
- if (!planet.isEnabled()) {
- planet.forceLine.setEnabled(false);
- planet.velocityLine.setEnabled(false);
- continue;
- }
- this.debugOutput(planet);
- }
- }
- if (scene && scene.activeCamera) {
- scene.render();
- }
- });
- // Resize
- window.addEventListener("resize", () => {
- this.engine.resize();
- });
- }
- };
- (async () => await new Runner().run())();
- </script>
- </body>
- </html>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement