Most performant method to implement static minimap?

Hey folks,

So I’m trying to implement a floorplan minimap for my scene. I want to be able to use the minimap in a static way in that the minimap “camera” does not follow the user but instead just shows the entire floorplan, always looking straight down with the same orientation, all the time. The only element of the minimap that is not static is the user’s marker icon, denoting the user’s position and y rotation in the scene.

I first tried implementing this with two FreeCameras and 2 corresponding viewports. One camera was static while the other was the main camera that is used to move around. The user marker mesh was parented under the dynamic FreeCamera, and so the user marker mesh was updated on the static FreeCamera’s viewport by utilizing layer masks. This worked really well from a UX perspective, especially because it enabled clicking on the floor mesh to navigate via the minimap camera.

However, this method seemed to cut the frame-rate in half and just about doubled the draw calls. I tried freezing the mesh world matrix for every mesh besides the user marker mesh, but this did not have noticeable improvement to the performance in this sense.

Thus, I am now interested in implementing the minimap in a more performant way, and so I am looking into grabbing a screenshot of the top-down camera’s viewport, and then saving that image as an “img” in HTML, and then overlaying the user’s orientation marker as an img on top of that. However, I am running into issues with converting the scene x, y coordinates to pixel coordinates, much less in a way that is consistent with the scale change.

Has anyone implemented an HTML version of a minimap before? Would you mind sharing your approach and best practices? Any playgrounds would be appreciated as well. OR, instead, if you know of a way to improve the performance of 2 camera viewports as the implementation of the minimap, so that the drawcalls aren’t really increased much and the fps does not get cut in half, then please share!!

Thanks for any help and advice in advance.

I’m learning from @sharp recently. I think his work is very amazing and worth learning.

1 Like

I would try:

  1. Remove from static camera render everything except user marker and check draw calls, they should be OK. If not - the problem is somewhere else.
  2. For minimap use simplified version of your model, with layer or at LOD level.
1 Like

I agree, that looks really good. But I don’t see any mention of his approach regarding the minimap, just the 360 panorama transitions?

@sharp Would you comfortable explaining how you approached the minimap in this? Interface interactive

Did you implement this in babylon or HTML? I’m trying to get the user marker to move consistently in the floorplan of my game but I cannot get it to match up/cannot convert from babylon viewport coordinates to html pixel values. Thanks!

1 Like

Thank you for the suggestions @labris. Regarding your suggestion to not render anything except user marker, wouldn’t that skip rendering everything else each frame? And what method/attribute of the camera do you recommend using to implement this? I cannot find an appropriate property or method of the camera class. Thanks!

Unfortunately, your second suggestion isn’t really viable in my use case. But that is a good suggestion, thanks

The method which I’ve mentioned is just simple layerMask - https://playground.babylonjs.com/#0LYEC3#4

What I meant is that for minimap you need to render dynamically only user marker (which is not rendered by the main camera, so the number of draw calls will be actually the same).
Then, depending on your application, you may add either the second oversimplified version of the model in vertical projection, or even use appropriate static image in a viewport’s layer background, as in @sebavan example from another thread).

3 Likes

@johntdaly7 yes I implemented it in HTML and use CSS animation to move the marker on the minimap.

For example to synchronize the camera orientation to the minimap, with JQuery :

scene.onAfterCameraRenderObservable.add(function() {
      let rotation = scene.activeCamera.rotation.y;
      $("#mini-map .mini-map-position").css("transform", "rotate(" + rotation + "rad)");
});

I take the simplest case scenario that is : the center of the minimap image is the center of your 3D scene.

You need a scale factor, for example if 10 pixels of the minimap is 5 meters in 3D then the scaleFactor will be 5/10.

If you want to update the camera position from a click made on the minimap, you first need to compute the absolute mouse position in the minimap container.

$minimapImage.on("pointerup", function(e) {
       e.preventDefault();
       e.stopImmediatePropagation();
       let absolutePosition = getAbsolutePosition(e.target);

       let x = e.clientX - absolutePosition.x;
       let y = e.clientY - absolutePosition.y;

       scene.activeCamera.position.x = x / scaleFactor;
       scene.activeCamera.position.z = -y / scaleFactor;
     });

To compute the absolute position of the minimap container I used this function but it may be not suit to all the cases :

function getAbsolutePosition(element) {
  var el = element;
  var x = 0;
  var y = 0;
  while (el) {
    if (el.tagName === "BODY") {
      // deal with browser quirks with body/window/document and page scroll
      var xScroll = el.scrollLeft || document.documentElement.scrollLeft;
      var yScroll = el.scrollTop || document.documentElement.scrollTop;

      x += (el.offsetLeft - xScroll + el.clientLeft);
      y += (el.offsetTop - yScroll + el.clientTop);
    } else {
      // for all other non-BODY elements
      x += (el.offsetLeft - el.scrollLeft + el.clientLeft);
      y += (el.offsetTop - el.scrollTop + el.clientTop);
    }
    el = el.offsetParent;
  }
  return {
    x: x,
    y: y
  };
}

If you want to update the marker position on the minimap from the babylon camera :

function updateMarker(x, y, z, duration) {
   $("#mini-map .mini-map-position").animate(
   {
      "left" : (x * scaleFactor) + "px",
      "top" : -(z * scaleFactor) + "px"
    }, duration);
}
1 Like

@johntdaly7 and yes it’s the most performant method by far to implement static minimap :slight_smile:

I am actually totally not sure it is the most performant way.

Doing through webgl should definitely be less resource intensive as it prevents any overdraw or skia compostion.

the overhead is only one extra draw call against all the 2d rendering + composition happening at the compositor level.

1 Like

Oh really ? I don’t know what is an overdraw and a skia composition :stuck_out_tongue: .IMO nothing could outperformed a scale and a translate of a 3D points to a 2D space but I trust your experience better than mine :slight_smile: