pmndrs / cannon-es-debugger Goto Github PK
View Code? Open in Web Editor NEWWireframe debugger for use with cannon-es https://github.com/react-spring/cannon-es
Home Page: https://pmndrs.github.io/cannon-es-debugger/
License: MIT License
Wireframe debugger for use with cannon-es https://github.com/react-spring/cannon-es
Home Page: https://pmndrs.github.io/cannon-es-debugger/
License: MIT License
Can i use it in web worker?How to do it?
As of new version of cannon-es Cylinder.type is equal to Shape.types.CYLINDER (as it should be).
But so far the debug-renderer handled Cylinders as ConvexPolyhedrons and now every cylinder is represented as the default "CylinderGeometry(0.5, 0.5, 1, 32)"
I'm not sure about the proper fix, but removing the body of "case CYLINDER" in "function createMesh()" leaving as
case CYLINDER: \n case CONVEXPOLYHEDRON: ...
solves the issue,
(the "function typeMatch()" also has not prepared to handle cylinders)
This may be an issue on my side, not grasping the complete concept of the debugger: What's the concept behind updateMesh
calling scaleMesh
every time - I'd rather expect scaleMesh
to be called only on mesh intialization?
Hey thanks for this library, its awesome and makes it easy to visualize the collision bodies. I was trying to enable a way to for this mode to be turned on and off but noticed that I would have to find the objects in the scene and manually remove them. When you stop calling update()
the objects remain in their old position.
I whipped something up will submit a PR if you are willing to consider it. Cheers.
Cannon.js' CannonDebugRenderer.js
require user to write cannonDebugRenderer.update()
.
But cannon-es-debugger.js
has autoUpdate
parameter and default set to true
, so will use inner requestAnimationFrame
to call update()
.
After migrated from cannon.js to cannon-es-debugger, be careful to set autoUpdate
to false
, or delete the old cannonDebugRenderer.update()
in own code.
cannon-es-debugger v: 1.0.0
cannon-es v: 0.18.0
threejs v: 0.136.0
when I update the data in a heightfield and call update on it and then call the debugger update, the visualization does not update.
I think this is the issue:
cannon-es-debugger/src/cannon-es-debugger.ts
Line 228 in 4525cae
When conditionally rendering the debugger it would be useful to be able to detach/disable it after it has been enabled, without having to reload the entire page. E.g. when used with dat.gui etc.
Currently there is no way to do this afaict. What would you think about adding a detach
function that removes all meshes from the scene?
let enabled = true
const debugger = cannonDebugger(scene, world.bodies, { autoUpdate: false })
requestAnimationFrame(() => {
if (enabled) {
debugger.update()
}
})
// This would be in an event listener in a real world scenario
enabled = false
debugger.detatch()
Saw that in https://github.com/swift502/Sketchbook the physics debugger uses different colors for shapes, thought it was kinda nice. So wondering if it's something we want to add?
This warning appears when I add cannon-es-debugger:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Example</title>
<style>
body {
margin: 0;
}
</style>
</head>
<body>
<!-- Since import maps are not yet supported by all browsers, it is
necessary to add the polyfill es-module-shims.js -->
<script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/[email protected]/build/three.module.min.js",
"orbit-controls": "https://unpkg.com/[email protected]/examples/jsm/controls/OrbitControls.js",
"cannon-es": "https://cdn.jsdelivr.net/npm/[email protected]/+esm",
"cannon-es-debugger": "https://cdn.jsdelivr.net/npm/[email protected]/+esm"
}
}
</script>
<script type="module" src="./js/index.js"></script>
</body>
</html>
index.js
import * as THREE from "three";
import { OrbitControls } from "orbit-controls";
import * as CANNON from "cannon-es";
import CannonDebugger from "cannon-es-debugger";
async function init() {
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(50,
window.innerWidth / window.innerHeight, 0.1, 500);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setClearColor(0x079bb0, 1);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const orbitControls = new OrbitControls(camera, renderer.domElement);
orbitControls.target = new THREE.Vector3(0, 0, 0);
const world = new CANNON.World({ gravity: new CANNON.Vec3(0, 0, 0) });
const cannonDebugger = new CannonDebugger(scene, world);
let dt;
const clock = new THREE.Clock();
window.onresize = () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
};
function render() {
requestAnimationFrame(render);
orbitControls.update();
dt = clock.getDelta();
world.step(dt);
cannonDebugger.update();
renderer.render(scene, camera);
}
render();
}
init();
Hi,
Does this cannon-es-debugger work with Three.js only or it should work with eg. BabylonJS?
Truly wonderful library (Cannon-es)
But I'm having a hard time seeing how to implement the debugger version.
Is there an example html file anywhere, that I can run?
Hi guys.
recently I use cannon-es-debugger with svelte(v.3.0.0)
but it said that "ReferenceError: three is not defined "
I don't know what it happened.
i just know it happened when imported cannon-es-debugger.
Hi! Is there a way we could make this module usable as a jsm module. Could we assume that the peer dependancies will be relative to this module? Or maybe pass three/cannon into the debugger?
I have been using the debugger like this as a jsm module:
// ./node_modules/cannon-es-debugger-jsm/index.js
import * as THREE from '../three/build/three.module.js'
import * as CANNON from '../cannon-es/dist/cannon-es.js'
// Cannon Debugger
export class CannonDebugRenderer {
constructor(scene, world, options) {
options = options || {};
this.scene = scene;
this.world = world;
this._meshes = [];
this._material = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true });
this._sphereGeometry = new THREE.SphereGeometry(1);
this._boxGeometry = new THREE.BoxGeometry(1, 1, 1);
this._planeGeometry = new THREE.PlaneGeometry( 10, 10, 10, 10 );
this._cylinderGeometry = new THREE.CylinderGeometry( 1, 1, 10, 10 );
this.tmpVec0 = new CANNON.Vec3();
this.tmpVec1 = new CANNON.Vec3();
this.tmpVec2 = new CANNON.Vec3();
this.tmpQuat0 = new CANNON.Vec3();
}
update(bodies){
// var bodies = this.world.bodies;
var meshes = this._meshes;
var shapeWorldPosition = this.tmpVec0;
var shapeWorldQuaternion = this.tmpQuat0;
var meshIndex = 0;
for (var i = 0; i !== bodies.length; i++) {
var body = bodies[i];
for (var j = 0; j !== body.shapes.length; j++) {
var shape = body.shapes[j];
this._updateMesh(meshIndex, body, shape);
var mesh = meshes[meshIndex];
if(mesh){
// Get world position
body.quaternion.vmult(body.shapeOffsets[j], shapeWorldPosition);
body.position.vadd(shapeWorldPosition, shapeWorldPosition);
// Get world quaternion
body.quaternion.mult(body.shapeOrientations[j], shapeWorldQuaternion);
// Copy to meshes
mesh.position.copy(shapeWorldPosition);
mesh.quaternion.copy(shapeWorldQuaternion);
}
meshIndex++;
}
}
for(var i = meshIndex; i < meshes.length; i++){
var mesh = meshes[i];
if(mesh){
this.scene.remove(mesh);
}
}
meshes.length = meshIndex;
}
_updateMesh(index, body, shape){
var mesh = this._meshes[index];
if(!this._typeMatch(mesh, shape)){
if(mesh){
this.scene.remove(mesh);
}
mesh = this._meshes[index] = this._createMesh(shape);
}
this._scaleMesh(mesh, shape);
}
_typeMatch(mesh, shape){
if(!mesh){
return false;
}
var geo = mesh.geometry;
return (
(geo instanceof THREE.SphereGeometry && shape instanceof CANNON.Sphere) ||
(geo instanceof THREE.BoxGeometry && shape instanceof CANNON.Box) ||
(geo instanceof THREE.PlaneGeometry && shape instanceof CANNON.Plane) ||
(geo.id === shape.geometryId && shape instanceof CANNON.ConvexPolyhedron) ||
(geo.id === shape.geometryId && shape instanceof CANNON.Trimesh) ||
(geo.id === shape.geometryId && shape instanceof CANNON.Heightfield)
);
}
_createMesh(shape){
var mesh;
var material = this._material;
switch(shape.type){
case CANNON.Shape.types.SPHERE:
mesh = new THREE.Mesh(this._sphereGeometry, material);
break;
case CANNON.Shape.types.BOX:
mesh = new THREE.Mesh(this._boxGeometry, material);
break;
case CANNON.Shape.types.PLANE:
mesh = new THREE.Mesh(this._planeGeometry, material);
break;
case CANNON.Shape.types.CONVEXPOLYHEDRON:
// Create mesh
var geo = new THREE.Geometry();
// Add vertices
for (var i = 0; i < shape.vertices.length; i++) {
var v = shape.vertices[i];
geo.vertices.push(new THREE.Vector3(v.x, v.y, v.z));
}
for(var i=0; i < shape.faces.length; i++){
var face = shape.faces[i];
// add triangles
var a = face[0];
for (var j = 1; j < face.length - 1; j++) {
var b = face[j];
var c = face[j + 1];
geo.faces.push(new THREE.Face3(a, b, c));
}
}
geo.computeBoundingSphere();
geo.computeFaceNormals();
mesh = new THREE.Mesh(geo, material);
shape.geometryId = geo.id;
break;
case CANNON.Shape.types.TRIMESH:
var geometry = new THREE.Geometry();
var v0 = this.tmpVec0;
var v1 = this.tmpVec1;
var v2 = this.tmpVec2;
for (var i = 0; i < shape.indices.length / 3; i++) {
shape.getTriangleVertices(i, v0, v1, v2);
geometry.vertices.push(
new THREE.Vector3(v0.x, v0.y, v0.z),
new THREE.Vector3(v1.x, v1.y, v1.z),
new THREE.Vector3(v2.x, v2.y, v2.z)
);
var j = geometry.vertices.length - 3;
geometry.faces.push(new THREE.Face3(j, j+1, j+2));
}
geometry.computeBoundingSphere();
geometry.computeFaceNormals();
mesh = new THREE.Mesh(geometry, material);
shape.geometryId = geometry.id;
break;
case CANNON.Shape.types.HEIGHTFIELD:
var geometry = new THREE.Geometry();
var v0 = this.tmpVec0;
var v1 = this.tmpVec1;
var v2 = this.tmpVec2;
for (var xi = 0; xi < shape.data.length - 1; xi++) {
for (var yi = 0; yi < shape.data[xi].length - 1; yi++) {
for (var k = 0; k < 2; k++) {
shape.getConvexTrianglePillar(xi, yi, k===0);
v0.copy(shape.pillarConvex.vertices[0]);
v1.copy(shape.pillarConvex.vertices[1]);
v2.copy(shape.pillarConvex.vertices[2]);
v0.vadd(shape.pillarOffset, v0);
v1.vadd(shape.pillarOffset, v1);
v2.vadd(shape.pillarOffset, v2);
geometry.vertices.push(
new THREE.Vector3(v0.x, v0.y, v0.z),
new THREE.Vector3(v1.x, v1.y, v1.z),
new THREE.Vector3(v2.x, v2.y, v2.z)
);
var i = geometry.vertices.length - 3;
geometry.faces.push(new THREE.Face3(i, i+1, i+2));
}
}
}
geometry.computeBoundingSphere();
geometry.computeFaceNormals();
mesh = new THREE.Mesh(geometry, material);
shape.geometryId = geometry.id;
break;
}
if(mesh){
this.scene.add(mesh);
}
return mesh;
}
_scaleMesh(mesh, shape){
switch(shape.type){
case CANNON.Shape.types.SPHERE:
var radius = shape.radius;
mesh.scale.set(radius, radius, radius);
break;
case CANNON.Shape.types.BOX:
mesh.scale.copy(shape.halfExtents);
mesh.scale.multiplyScalar(2);
break;
case CANNON.Shape.types.CONVEXPOLYHEDRON:
mesh.scale.set(1,1,1);
break;
case CANNON.Shape.types.TRIMESH:
mesh.scale.copy(shape.scale);
break;
case CANNON.Shape.types.HEIGHTFIELD:
mesh.scale.set(1,1,1);
break;
}
}
};
I have a requestAnimationFrame in my project where I do the three.js rendering and the cannon-es physics stepping. However when setting up cannon-es-debugger, this happens:
I keep the mesh in sync with the body, however the wireframes are just 1 step behind.
To fix this, we should provide an autoUpdate
which the user can turn off if he needs to and then expose an update()
method.
This had worked fine when using non-ES official cannon.js
var physicsMaterial = new CANNON.Material("slipperyMaterial");
var physicsContactMaterial = new CANNON.ContactMaterial(physicsMaterial, physicsMaterial, 0.0, 0.3);
world.addContactMaterial(physicsContactMaterial);
var groundShape = new CANNON.Plane();
var groundBody = new CANNON.Body({ mass: 0}), material: physicsMaterial });
groundBody.addShape(groundShape);
groundBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1,0,0),-Math.PI/2 - .30);
groundBody.position.set(0,0,0);
world.addBody(groundBody);
Now I am getting this error
Uncaught TypeError: Cannot use 'in' operator to search for 'friction' in 0
at Function.defaults (main.js:2)
at new Ac (main.js:2)
at main.js:2
at main.js:2
defaults @ main.js:2
Ac @ main.js:2
(anonymous) @ main.js:2
(anonymous) @ main.js:2
Perhaps I was looking at outdated docs or examples when I pulled this in. Any ideas?
Three.js r125 removed the Geometry
class that cannon-es-debugger appears to depend on. I think for the time being it can still be imported from examples.
In this particular example they also seem to be the wrong size.
https://codesandbox.io/s/raycast-vehicle-forked-77rss?file=/src/App.js
Would you please
add mesh.scale.set(1 * scale, 1 * scale, 1 * scale)
after line 188
and
change line 260 from mesh.position.copy(shapeWorldPosition as unknown as ThreeVector3)
to
mesh.position.copy(shapeWorldPosition.clone().multiplyScalar(scale) as unknown as ThreeVector3)
(or something more elegant, sorry I don't code in TypeScript)
Cheers
Rai
The usage page uses the example where the CannonDebugger
is a class, which is not right because right now what is exported from this package is a function. In TypeScript it's not possible to call it with new
keyword, because it will throw an error as follows:
new CannonDebugger(scene, world);
// 'new' expression, whose target lacks a construct signature, implicitly has an 'any' type.ts(7009)
In order to make it work in TypeScript, it would have to be called like a regular function
CannonDebugger(scene, world); // does not throw an error
What is more the types from the declaration files seem to be outdated too. When I checked the dist/cannon-es-debugger.d.ts
, it also points that the CannonDebugger is no more a class.
declare module "cannon-es-debugger" {
import { Shape } from 'cannon-es';
import { Mesh } from 'three';
import type { Body, World } from 'cannon-es';
import type { Scene, Color } from 'three';
export type DebugOptions = {
color?: string | number | Color;
scale?: number;
onInit?: (body: Body, mesh: Mesh, shape: Shape) => void;
onUpdate?: (body: Body, mesh: Mesh, shape: Shape) => void;
};
export default function CannonDebugger(scene: Scene, world: World, { color, scale, onInit, onUpdate }?: DebugOptions): {
update: () => void;
};
}
The solution would be:
CannonDebugger
to createCannonDebugger
and use named exports so that it is not confusing anymore that whether it's a class or function.If you agree with this I can prepare a PR for that
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.