Closed 8Observer8 closed 1 year ago
Did you miss https://lusito.github.io/box2d.ts/docs/guide/debug-draw/ or is it not good enough?
I tried your solution but it is very inconvenient for pure WebGL.
1) I can't set an arbitrary coordinate center on the debug canvas like I do with glMatrix.ortho(out, left, right, bottom, top, near, far) 2) But I need the colliders to be drawn behind the game objects in order to see the boundaries of the sprites
Why not pass the vertex coordinates to the DrawSolidPolygon method so I can draw the borders myself with pure WebGL?
debug_drawer.py
from Box2D import b2Draw
from OpenGL.GL import *
from PyQt6.QtGui import QMatrix4x4, QQuaternion, QVector3D
class DebugDrawer(b2Draw):
def __init__(self, program, worldScale):
super().__init__()
self.program = program
self.WORLD_SCALE = worldScale
self.mvpMatrixLocation = self.program.uniformLocation("uMvpMatrix")
self.colorLocation = self.program.uniformLocation("uColor")
self.modelMatrix = QMatrix4x4()
self.projMatrix = None
self.viewMatrix = None
self.projViewMatrix = None
self.color = None
self.lineWidth = 4
def DrawSolidPolygon(self, vertexes, color):
print("Polygon. Begin")
print(vertexes[0][0] * 30, vertexes[0][1] * 30)
print(vertexes[1][0] * 30, vertexes[1][1] * 30)
print(vertexes[2][0] * 30, vertexes[2][1] * 30)
print(vertexes[3][0] * 30, vertexes[3][1] * 30)
print("Polygon. End")
# My custom drawing with OpenGL
def DrawPolygon(self, vertexes, color):
pass
def DrawSegment(self, p1, p2, color):
pass
def DrawPoint(self, p, size, color):
pass
def DrawCircle(self, center, radius, color, drawwidth=1):
pass
def DrawSolidCircle(self, center, radius, axis, color):
pass
def DrawTransform(self, xf):
pass
I usually use 2 canvases. One for drawing the game and one for the debug draw.
But if that doesn't work for you, take a look at the implementation, it's very little code. Feel free to write your own implementation of the b2Draw interface.
I understand, that you'd ideally only want to implement the drawsolidpolygon method, but that would require me to actually calculate all the vertices. The canvas implementation does not need this and is much simpler this way.
Feel free to create a PR with necessary changes.
But if that doesn't work for you, take a look at the implementation, it's very little code. Feel free to write your own implementation of the b2Draw interface.
It's exactly what I need! It works! Now I can draw colliders on one canvas with pure WebGL 1.0. Thanks very much!
npm i -g browserify parcel typescript uglify-js npm i @box2d/core gl-matrix npm i @types/gl-matrix
src/debug-drawer.ts
import { b2Draw, b2Transform, RGBA, XY } from "@box2d/core";
// This class implements debug drawing callbacks that are invoked inside b2World::Step
export default class DebugDrawer implements b2Draw {
private readonly gl: WebGLRenderingContext;
public constructor(gl: WebGLRenderingContext) {
this.gl = gl;
}
DrawSolidPolygon(vertices: XY[], vertexCount: number, color: RGBA): void {
console.log(vertices);
console.log(color);
}
PushTransform(xf: b2Transform): void {}
PopTransform(xf: b2Transform): void {}
DrawPolygon(vertices: XY[], vertexCount: number, color: RGBA): void {}
DrawCircle(center: XY, radius: number, color: RGBA): void {}
DrawSolidCircle(center: XY, radius: number, axis: XY, color: RGBA): void {}
DrawSegment(p1: XY, p2: XY, color: RGBA): void {}
DrawTransform(xf: b2Transform): void {}
DrawPoint(p: XY, size: number, color: RGBA): void {}
}
src/main.ts
import { b2BodyType, b2PolygonShape, b2World, DrawShapes } from "@box2d/core";
import DebugDrawer from "./debug-drawer";
import { gl, initWebGLContext } from "./webgl-context";
const world = b2World.Create({ x: 0, y: 9.8 });
const WORLD_SCALE = 30;
let debugDrawer;
function gameLoop() {
requestAnimationFrame(gameLoop);
world.Step(0.016, { velocityIterations: 8, positionIterations: 3 });
gl.clear(gl.COLOR_BUFFER_BIT); // Clear canvas
DrawShapes(debugDrawer, world); // Draw colliders
}
async function init() {
if (!initWebGLContext("renderCanvas")) return;
debugDrawer = new DebugDrawer(gl);
const groundShape = new b2PolygonShape();
groundShape.SetAsBox(100 / WORLD_SCALE, 10 / WORLD_SCALE);
const groundBody = world.CreateBody({ type: b2BodyType });
groundBody.CreateFixture({ shape: groundShape });
// console.log(groundBody);
gameLoop();
}
init()
src/webgl-context.ts
export let gl: WebGLRenderingContext;
export function initWebGLContext(canvasName)
{
const canvas = document.getElementById(canvasName);
if (canvas === null)
{
console.log(`Failed to get a canvas element with the name "${canvasName}"`);
return false;
}
gl = (canvas as HTMLCanvasElement).getContext("webgl", { alpha: false, premultipliedAlpha: false }) as WebGLRenderingContext;
return true;
}
public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Example</title>
</head>
<body>
<canvas id="renderCanvas" width="500" height="500"></canvas>
<script type="module" src="js/bundle.js"></script>
</body>
</html>
package.json
{
"name": "debug-drawer-box2dcore-ts",
"version": "1.0.0",
"description": "",
"main": "public/js/bundle.js",
"targets": {
"main": {
"includeNodeModules": true
}
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"del_files": "del /f /q /s .\\public\\js\\*.*",
"dev": "npm run del_files && parcel watch src/main.ts",
"compile": "tsc -p tsconfigs/tsconfig.release.json",
"bundle": "browserify public/js/main.js -o public/js/bundle.js",
"uglify": "uglifyjs public/js/bundle.js -o public/js/bundle.js",
"release": "npm run compile && npm run bundle && npm run uglify"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@box2d/core": "^0.10.0",
"gl-matrix": "^3.4.3"
},
"devDependencies": {
"@types/gl-matrix": "^3.2.0"
}
}
tsconfigs/tsconfig.json
{
"compilerOptions": {
"target": "ES5",
"outDir": "../public/js",
"sourceMap": false,
"types": [
"node"
],
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": [
"../src/**/*.ts"
],
"exclude": [
"node_modules"
]
}
I'm trying to build the above example for release using the npm run release command from package.json, but I'm getting the following errors:
E:_Projects\Physics\box2d-core\debug-drawer-box2dcore-webgl-ts>npm run release
debug-drawer-box2dcore-webgl-ts@1.0.0 release E:_Projects\Physics\box2d-core\debug-drawer-box2dcore-webgl-ts npm run compile && npm run bundle && npm run uglify
debug-drawer-box2dcore-webgl-ts@1.0.0 compile E:_Projects\Physics\box2d-core\debug-drawer-box2dcore-webgl-ts tsc -p tsconfigs/tsconfig.release.json
src/main.ts:25:43 - error TS2322: Type 'typeof b2BodyType' is not assignable to type 'b2BodyType'.
25 const groundBody = world.CreateBody({ type: b2BodyType });
../../../node_modules/@box2d/core/dist/dynamics/b2_body.d.ts:27:5
27 type?: b2BodyType;
The expected type comes from property 'type' which is declared here on type 'b2BodyDef'
Found 1 error in src/main.ts:25
npm ERR! code ELIFECYCLE
npm ERR! errno 2
npm ERR! debug-drawer-box2dcore-webgl-ts@1.0.0 compile: tsc -p tsconfigs/tsconfig.release.json
npm ERR! Exit status 2
npm ERR!
npm ERR! Failed at the debug-drawer-box2dcore-webgl-ts@1.0.0 compile script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm WARN Local package.json exists, but node_modules missing, did you mean to install?
npm ERR! A complete log of this run can be found in:
npm ERR! C:\Users\8Observer8\AppData\Roaming\npm-cache_logs\2023-01-19T02_13_40_122Z-debug.log
npm ERR! code ELIFECYCLE
npm ERR! errno 2
npm ERR! debug-drawer-box2dcore-webgl-ts@1.0.0 release: npm run compile && npm run bundle && npm run uglify
npm ERR! Exit status 2
npm ERR!
npm ERR! Failed at the debug-drawer-box2dcore-webgl-ts@1.0.0 release script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm WARN Local package.json exists, but node_modules missing, did you mean to install?
npm ERR! A complete log of this run can be found in: npm ERR! C:\Users\8Observer8\AppData\Roaming\npm-cache_logs\2023-01-19T02_13_40_555Z-debug.log
It is something with b2BodyType
: error TS2322: Type 'typeof b2BodyType' is not assignable to type 'b2BodyType'.
@Lusito, can I use @box2d/core with JavaScript?
box2d-wasm has the b2Draw class with JavaScript and ES6 module support.
Official example: https://github.com/Birch-san/box2d-wasm/tree/master/demo/modern/public
My example:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<!-- Since import maps are not yet supported by all browsers, it is
necessary to add the polyfill es-module-shims.js
Source: https://threejs.org/docs/index.html#manual/en/introduction/Installation
-->
<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
<script type="importmap">
{
"imports": {
"gl-matrix": "https://cdn.skypack.dev/gl-matrix@3.4.3",
"box2d": "https://8observer8.github.io/lib/box2d-wasm-2.4.1/box2d-wasm-2.4.1.min.js"
}
}
</script>
<script type="module">
import Box2DLib from "box2d";
let box2d;
function initBox2D() {
return new Promise(resolve => {
Box2DLib().then((re) => {
box2d = re;
resolve();
});
});
}
async function main() {
await initBox2D();
const {
b2Draw: { e_shapeBit },
b2Vec2,
JSDraw
} = box2d;
const vec = new b2Vec2(1, 2);
console.log(`vec = ${vec.x}, ${vec.y}`);
function makeDebugDrawer() {
const debugDrawer = Object.assign(new JSDraw(), {
DrawSegment(vert1_p, vert2_p, color_p) {},
DrawPolygon(vertices_p, vertexCount, color_p) {},
DrawSolidPolygon(vertices_p, vertexCount, color_p) {},
DrawCircle(center_p, radius, color_p) {},
DrawSolidCircle(center_p, radius, axis_p, color_p) {},
DrawTransform(transform_p) {},
DrawPoint(vertex_p, sizeMetres, color_p) {}
});
debugDrawer.SetFlags(e_shapeBit);
return debugDrawer;
}
const debudDrawer = makeDebugDrawer();
}
main();
</script>
</body>
</html>
None of these issues are related to this topic. Ideally, they should be separate issues.
Aside from that, quickly:
So yes, you can use it with just javascript
The challenge is to replace Planck.js with @box2d/core in this example: https://plnkr.co/edit/nFHRxUFhcKhL4Jpd
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<canvas id="renderCanvas" width="256" height="256"></canvas>
<script type="importmap">
{
"imports": {
"gl-matrix": "https://cdn.skypack.dev/gl-matrix@3.4.3",
"planck-js": "https://cdn.skypack.dev/planck-js@0.3.29"
}
}
</script>
<script type="module">
import { mat4, vec3 } from "gl-matrix";
import * as pl from "planck-js";
const gravity = pl.Vec2(0, 0);
const world = pl.World(gravity);
const output = document.createElement("div");
output.innerText = `gravity = ${world.getGravity()}`;
document.body.appendChild(output);
const gl = document.getElementById("renderCanvas").getContext("webgl2");
gl.clearColor(0.2, 0.2, 0.2, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
</script>
</body>
</html>
Could you create a CDN link?
Not sure what the issue is for creating the CDN link. Just replace package name and version and you have the new CDN link.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Document</title>
</head>
<body>
<canvas id="renderCanvas" width="256" height="256"></canvas>
<script type="importmap">
{
"imports": {
"gl-matrix": "https://cdn.skypack.dev/gl-matrix@3.4.3",
"@box2d/core": "https://cdn.skypack.dev/@box2d/core@0.10.0"
}
}
</script>
<script type="module">
import { mat4, vec3 } from "gl-matrix";
import { b2World, b2Vec2 } from "@box2d/core";
const gravity = new b2Vec2(0, 0);
const world = b2World.Create(gravity);
const output = document.createElement("div");
output.innerText = `gravity = ${JSON.stringify(world.GetGravity())}`;
document.body.appendChild(output);
const gl = document.getElementById("renderCanvas").getContext("webgl2");
gl.clearColor(0.2, 0.2, 0.2, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
</script>
</body>
</html>
Wow, it's cool! Many thanks!
About your type issue:
// Wrong:
const groundBody = world.CreateBody({ type: b2BodyType });
// Correct:
const groundBody = world.CreateBody({
type: b2BodyType.b2_staticBody // or b2_kinematicBody or b2_dynamicBody, depending on your use-case
});
Thank you very much! It's awesome! I tried to build my example and it works!
This line of code should be added to index.html
:
<!-- Since import maps are not yet supported by all browsers, it is
necessary to add the polyfill es-module-shims.js
Source: https://threejs.org/docs/index.html#manual/en/introduction/Installation
-->
<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
I have the problem with JavaScript code: DrawSolidPolygon gets wrong collider position
Please, implement b2Draw, SetDebugDraw, and DebugDraw like it was implemented in pybox2d