Skip to content

Commit

Permalink
Improve gravity example
Browse files Browse the repository at this point in the history
  • Loading branch information
DrA1ex committed Sep 28, 2023
1 parent da55066 commit aed766b
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 33 deletions.
10 changes: 10 additions & 0 deletions examples/common/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,4 +190,14 @@ export function unionBox(dst, box) {
const bottom = Math.max(dst.bottom, box.bottom);

dst.update(left, right, top, bottom);
}

export function bmRandom() {
let u = 0, v = 0;
while (u === 0) u = Math.random(); //Converting [0,1) to (0,1)
while (v === 0) v = Math.random();
let num = Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v);
num = num / 10.0 + 0.5; // Translate to 0 -> 1
if (num > 1 || num < 0) return bmRandom() // resample between 0 and 1
return num
}
105 changes: 76 additions & 29 deletions examples/gravity/index.js
Original file line number Diff line number Diff line change
@@ -1,47 +1,91 @@
import {Vector2} from "../../lib/utils/vector.js";
import {BoundaryBox} from "../../lib/physics/common/boundary.js";
import {CircleBody} from "../../lib/physics/body/circle.js";
import {InsetConstraint} from "../../lib/physics/constraint.js";
import {WebglRenderer} from "../../lib/render/renderer/webgl/renderer.js";
import {SpriteObject} from "../../lib/render/renderer/webgl/objects/sprite.js";
import {ImageTexture} from "../../lib/render/renderer/webgl/misc/texture.js";
import {m4} from "../../lib/render/renderer/webgl/utils/m4.js";

import {Bootstrap} from "../common/bootstrap.js";
import * as Params from "../common/params.js";
import * as Utils from "../common/utils.js";
import {ResistanceForce} from "../../lib/physics/force.js";
import {InsetConstraint} from "../../lib/physics/constraint.js";

const options = Params.parse()
const options = Params.parse({
resistance: 1,
restitution: 0.2,
friction: 0.5
})
const BootstrapInstance = new Bootstrap(
new WebglRenderer(document.getElementById("canvas"), options),
Object.assign({solverBias: 0.1}, options)
Object.assign({solverBias: 0.5, solverBeta: 1}, options)
);

const WorldRect = new BoundaryBox(0, BootstrapInstance.canvasWidth, 0, BootstrapInstance.canvasHeight);
BootstrapInstance.addConstraint(new InsetConstraint(WorldRect, 0.3));
BootstrapInstance.addRect(WorldRect);

const {count, minSize, maxSize, gravity} = Params.parseSettings({
count: {parser: Params.Parser.int, param: "count", default: 100},
minSize: {parser: Params.Parser.int, param: "min_size", default: 5},
maxSize: {parser: Params.Parser.int, param: "max_size", default: 10},
gravity: {parser: Params.Parser.float, param: "gravity", default: 10}
const {
count, minSize, maxSize, gravity, particleScale, worldScale, minInteractionDistance
} = Params.parseSettings({
count: {parser: Params.Parser.int, param: "count", default: 200},
minSize: {parser: Params.Parser.int, param: "min_size", default: 10},
maxSize: {parser: Params.Parser.int, param: "max_size", default: 20},
gravity: {parser: Params.Parser.float, param: "gravity", default: 10},
particleScale: {parser: Params.Parser.float, param: "p_scale", default: 10},
worldScale: {parser: Params.Parser.float, param: "w_scale", default: 20},
minInteractionDistance: {parser: Params.Parser.float, param: "scale", default: 0.01 ** 2},
});

const WorldRect = new BoundaryBox(
-worldScale * BootstrapInstance.canvasWidth / 2,
worldScale * BootstrapInstance.canvasWidth / 2,
-worldScale * BootstrapInstance.canvasHeight / 2,
worldScale * BootstrapInstance.canvasHeight / 2
);

let projMatrix = m4.projection(BootstrapInstance.canvasWidth, BootstrapInstance.canvasHeight, 2);
projMatrix = m4.translate(projMatrix, BootstrapInstance.canvasWidth / 2, BootstrapInstance.canvasHeight / 2, 0);
projMatrix = m4.scale(projMatrix, 1 / worldScale, 1 / worldScale, 1);

BootstrapInstance.renderer.setProjectionMatrix(projMatrix)
BootstrapInstance.addConstraint(new InsetConstraint(WorldRect))
BootstrapInstance.addForce(new ResistanceForce(options.resistance));

BootstrapInstance.renderer.setBlending(WebGL2RenderingContext.SRC_COLOR, WebGL2RenderingContext.ONE);

const particleTexture = new ImageTexture(new URL("./sprites/particle.png", import.meta.url));
particleTexture.glWrapS = WebGL2RenderingContext.CLAMP_TO_EDGE;
particleTexture.glWrapT = WebGL2RenderingContext.CLAMP_TO_EDGE;
particleTexture.glMin = WebGL2RenderingContext.LINEAR_MIPMAP_LINEAR;
particleTexture.glMag = WebGL2RenderingContext.LINEAR_MIPMAP_LINEAR;

await particleTexture.wait();

const minRadius = Math.min(WorldRect.width, WorldRect.height) / 2 * 0.6
const maxRadius = Math.min(WorldRect.width, WorldRect.height) / 2 * 0.8;

for (let i = 0; i < count; i++) {
const angle = Math.random() * Math.PI * 2;
const size = minSize + Math.random() * Math.max(0, maxSize - minSize);

const position = new Vector2(
BootstrapInstance.canvasWidth * Math.random(),
BootstrapInstance.canvasHeight * Math.random()
);
const radius = minRadius + Math.random() * (maxRadius - minRadius);
const position = Vector2.fromAngle(angle)
.scale(radius)
.add(WorldRect.center);

Utils.clampBodyPosition(position, WorldRect, size);
const body = new CircleBody(position.x, position.y, size / 2, size);
const body = new CircleBody(position.x, position.y, size)
.setMass(size)
.setVelocity(Vector2.fromAngle(Math.random() * Math.PI * 2).scale(gravity))
.setFriction(options.friction)
.setRestitution(options.restitution)
.setTag("particle");

const renderer = new SpriteObject(body);
renderer.texture = particleTexture;
renderer.color = Utils.randomColor(170, 255);
renderer.scale = particleScale;

const {renderer} = BootstrapInstance.addRigidBody(
body.setFriction(options.friction)
.setTag("particle")
);
BootstrapInstance.addRigidBody(body, renderer);

renderer.color = Utils.randomColor(100, 200);
}

BootstrapInstance.enableHotKeys();
Expand Down Expand Up @@ -77,12 +121,11 @@ function processNode(node) {
const l2Boundary = BoundaryBox.fromBodies(l2Items);
const mass2 = l2Items.reduce((p, c) => p + c.mass, 0);

const impulse = calculateForce(l1Boundary.center, l2Boundary.center, stepDelta);
const impulse = calculateForce(l1Boundary.center, l2Boundary.center, stepDelta).scale(mass1 * mass2);

const impulse1 = impulse.scaled(mass2);
for (const item of l1Items) applyForce(item, impulse1)
for (const item of l1Items) applyForce(item, impulse)

const impulse2 = impulse.scaled(-mass1);
const impulse2 = impulse.scaled(-1)
for (const item of l2Items) applyForce(item, impulse2)
}

Expand All @@ -103,17 +146,21 @@ function processLeaf(leaf) {
const p1 = items[i];
const p2 = items[j];

const impulse = calculateForce(p1.position, p2.position, stepDelta);
const impulse = calculateForce(p1.position, p2.position, stepDelta)
.scale(p1.mass * p2.mass);

applyForce(p1, impulse.scaled(p2.mass));
applyForce(p2, impulse.scaled(p1.mass).negate());
applyForce(p1, impulse);
applyForce(p2, impulse.negate());
}
}
}

function calculateForce(p1, p2, stepDelta) {
const delta = p1.delta(p2);
const distSqr = delta.lengthSquared();
if (distSqr < minInteractionDistance) {
return new Vector2();
}

const force = -gravity / distSqr;
return delta.scale(force * stepDelta);
Expand Down
Binary file added examples/gravity/sprites/particle.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions examples/gravity/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ body {
box-sizing: border-box;
margin: 0;
padding: 1rem;
background: black;
}

#canvas {
Expand Down
5 changes: 3 additions & 2 deletions lib/render/renderer/webgl/objects/sprite.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const GL = WebGL2RenderingContext;
export class SpriteObject extends ColoredObject {
/*** @type {ITextureSource} */
texture;
scale = 1;

constructor(body) {
super(body);
Expand Down Expand Up @@ -48,8 +49,8 @@ export class SpriteObjectLoader {
positionBuffer[i * 4 + 2] = obj.z;
positionBuffer[i * 4 + 3] = obj.body.angle;

sizeBuffer[i * 2] = b.width;
sizeBuffer[i * 2 + 1] = b.height;
sizeBuffer[i * 2] = b.width * obj.scale;
sizeBuffer[i * 2 + 1] = b.height * obj.scale;

++i;
}
Expand Down
6 changes: 5 additions & 1 deletion lib/render/renderer/webgl/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ export class WebglRenderer extends IRenderer {
);
}

setBlending(sFactor, dFactor) {
this.#gl.blendFunc(sFactor, dFactor);
}

#render(key, objs) {
if (objs.size === 0) return;

Expand Down Expand Up @@ -125,7 +129,7 @@ export class WebglRenderer extends IRenderer {
this.#gl.clear(this.#gl.COLOR_BUFFER_BIT);

this.#gl.enable(this.#gl.BLEND);
this.#gl.blendFunc(this.#gl.ONE, this.#gl.ONE_MINUS_SRC_ALPHA);
this.setBlending(this.#gl.ONE, this.#gl.ONE_MINUS_SRC_ALPHA)

this.#gl.pixelStorei(this.#gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);

Expand Down
2 changes: 1 addition & 1 deletion lib/utils/color.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export function parseHexColor(color) {
return [r, g, b];
}

function rgbToHex(...colors) {
export function rgbToHex(...colors) {
return "#" + colors.map(
v => Math.round(Math.max(0, Math.min(1, v) * 255))
.toString(16)
Expand Down

0 comments on commit aed766b

Please sign in to comment.