* Draw a Mesh's world-space 3D bounding box as a green wireframe * under a `Camera3d`. The 8 corners of `mesh.getBounds3d()` are projected to * screen via `camera.worldToScreen`, then the 12 edges are stroked in a * screen-space pass (identity transform + a screen-ortho projection) so the
(renderer, panel, mesh, camera)
| 71 | * @param {import("melonjs").Camera3d} camera |
| 72 | */ |
| 73 | function strokeMeshWireframe(renderer, panel, mesh, camera) { |
| 74 | const box = mesh.getBounds3d(); |
| 75 | if (!box.isFinite()) { |
| 76 | return; |
| 77 | } |
| 78 | const min = box.min; |
| 79 | const max = box.max; |
| 80 | // near face (z = min): 0..3, far face (z = max): 4..7 |
| 81 | _meshCorners[0].set(min.x, min.y, min.z); |
| 82 | _meshCorners[1].set(max.x, min.y, min.z); |
| 83 | _meshCorners[2].set(max.x, max.y, min.z); |
| 84 | _meshCorners[3].set(min.x, max.y, min.z); |
| 85 | _meshCorners[4].set(min.x, min.y, max.z); |
| 86 | _meshCorners[5].set(max.x, min.y, max.z); |
| 87 | _meshCorners[6].set(max.x, max.y, max.z); |
| 88 | _meshCorners[7].set(min.x, max.y, max.z); |
| 89 | for (let i = 0; i < 8; i++) { |
| 90 | // worldToScreen returns null for a corner at/behind the camera — |
| 91 | // projecting it would mirror the point and draw edges shooting across |
| 92 | // the screen, so skip the whole box when the mesh straddles the camera. |
| 93 | if (camera.worldToScreen(_meshCorners[i], _meshScreen[i]) === null) { |
| 94 | return; |
| 95 | } |
| 96 | } |
| 97 | |
| 98 | // stroke the edges in screen space: drop the camera view transform and |
| 99 | // swap the perspective projection for a screen ortho, so the already- |
| 100 | // projected pixel coordinates draw 1:1. The projection isn't part of the |
| 101 | // save/restore stack, so it's saved/restored explicitly (same pattern as |
| 102 | // the renderer's own blit path). |
| 103 | renderer.save(); |
| 104 | _meshSavedProj.copy(renderer.projectionMatrix); |
| 105 | renderer.currentTransform.identity(); |
| 106 | _meshScreenProj.ortho(0, camera.width, camera.height, 0, -1, 1); |
| 107 | renderer.setProjection(_meshScreenProj); |
| 108 | renderer.setColor("green"); |
| 109 | renderer.lineWidth = 1; |
| 110 | for (const [a, b] of BOX_EDGES) { |
| 111 | renderer.strokeLine( |
| 112 | _meshScreen[a].x, |
| 113 | _meshScreen[a].y, |
| 114 | _meshScreen[b].x, |
| 115 | _meshScreen[b].y, |
| 116 | ); |
| 117 | } |
| 118 | // flush the lines under the screen projection before restoring the |
| 119 | // perspective projection, or they'd be re-projected on the next flush. |
| 120 | renderer.flush(); |
| 121 | renderer.setProjection(_meshSavedProj); |
| 122 | renderer.restore(); |
| 123 | panel.counters.inc("shapes"); |
| 124 | } |
| 125 | |
| 126 | /** |
| 127 | * Stroke the orange body AABB and the red collision shapes for the |
no test coverage detected