Hi there!
I don’t know guys how you are debugging your realtime code but I believe sooner or later everyone will get sick of console.log
I think everyone had an idea to display debugging information directly onto the canvas using BabylonJS GUI and so I did. This is a small utitlity class which allows you to easily display multiple information on any object you provide directly on the rendering canvas.
This is how it looks in action:
The Playground:
https://playground.babylonjs.com/#UNL3H0#4
You create an instance by simply calling:
const c3d = VisualConsole
c3d.create(engine, scene)
Or if you already have a full screen gui texture created pass it as the third parameter.
Than you can simply
c3d.log('Camera alpha - bound', camera, 'alpha')
and your camera alpha will be displayed on every frame in a debug frame. The first one on the picture.
This logs the position of the box every frame and since it is a Vector3, you get x, y, z in the debug frame. It is the second frame.
c3d.log('Box position - bound', box, 'position')
You can observe a Vector3 object itself:
const v = new BABYLON.Vector3(0, 0, 0)
c3d.log('Vector - bound', v)
It can deal with colors as well. The log60
is a convenience method for updating the value every 60 FPS. Check the source code for more methods like this.
const c = new BABYLON.Color3(1, 0, 0)
c3d.log60('Color - every 60 frame - bound', c)
What if you want the debug frame to follow your mesh so you have better visual feedback? Just use logf
(floating).
c3d.logf('Mesh - highlite at x > 0.4', box)
You want to be notified when a certain value is hit? Use method chaining and the highlite
method:
c3d.logf('Mesh - highlite at x > 0.4', box).highlite((value: BABYLON.AbstractMesh) => value.position.x > 0.4)
Do you want to start the debugger when a condition is met? Use the debug
method. This will not open the Developer tools for you, it is just a plain debugger
statement being hit in the code so open the Developer tools before testing this one:
c3d.log('Mesh - debug at x > 1', box).debug((value: BABYLON.AbstractMesh) => value.position.x > 1)
You have to call these methods only once.
If you want to manually update the values in the debugging frames, you can use log
every frame. The first parameter must remain the same, otherwise a new frame will be created:
scene.onBeforeRenderObservable.add(() => {
c3d.log('Random', Math.random())
c3d.log('Camera alpha - manual update', camera.alpha.toString())
c3d.log('Camera beta - manual update', camera.beta.toString())
})
The buttons allows you to toggle the location of the panel and D opens the BabylonJS Scene explorer and Inspector.
You can introduce new debugging frame types by adding in the createMappings
methods. The green frames are wider because the default value of the width is overridden in the mappings
.
entityTypeMappings.set(LoggedEntityType.Vector3, {
color: new BABYLON.Color3(0.0, 0.7, 0.1),
drawFunction: entity => this._drawVector3(entity),
width: 200
})
and implement a coresponding method like for example:
private _drawFloat(entity: LoggedEntity) {
if (entity.textInputs) {
entity.textInputs[0].text = VisualConsole._getObject<number>(entity).toString()
}
}
private _drawBoolean(entity: LoggedEntity) {
if (entity.textInputs) {
entity.textInputs[0].text = VisualConsole._getObject<boolean>(entity).toString()
}
}
private _drawMesh(entity: LoggedEntity) {
if (entity.textInputs) {
const obj = VisualConsole._getObject<BABYLON.AbstractMesh>(entity)
entity.textInputs[0].text = `${obj.position.x.toFixed(FLOAT_PRECISION)}, ${obj.position.y.toFixed(FLOAT_PRECISION)}, ${obj.position.z.toFixed(
FLOAT_PRECISION
)} `
entity.textInputs[1].text = `${obj.rotation.x.toFixed(FLOAT_PRECISION)}, ${obj.rotation.y.toFixed(FLOAT_PRECISION)}, ${obj.rotation.z.toFixed(
FLOAT_PRECISION
)} `
entity.textInputs[2].text = `${obj.scaling.x.toFixed(FLOAT_PRECISION)}, ${obj.scaling.y.toFixed(FLOAT_PRECISION)}, ${obj.scaling.z.toFixed(
FLOAT_PRECISION
)} `
}
}
Please note, all the values are displayed in text inputs so you can copy them out easily.
Some values to tweak:
const FLOAT_PRECISION = 4
const MAIN_PANEL_WIDTH = 0.4
const DEFAULT_PANEL_WIDTH = 160
const DEFAULT_PANEL_COLOR = Color3.Gray()
const DEBUGGER_HIT_PANEL_COLOR = Color3.Red()
const HIGHLITE_HIT_PANEL_COLOR = Color3.Purple()
const MESH_BADGE_LINK_X_OFFSET = -200
const MESH_BADGE_LINK_Y_OFFSET = -200
const MAIN_PANEL_ALPHA = 0.4
const LINES_ALPHA = 0.4
This small class contains only the basics I need, I already started to create a version which stores the history of the observed objects and one can display the last X positions of an object at once, so for example the cube is rendered X times leaving a trail every frame. I also tried to move this to a second scene, override the original scene’s renderloop allowing me to pause the scene or go backwards but this topic is so complicated that it works only in basic scenes which are strictly using only observables. I will be happy to share it with you when the time comes!
Hopefully guys you will find this usefull!
Thanks!
R.