Convergence

Creating a city building simulator

Recommended Posts

Thanks to the awesome Babylon framework, I'm creating a city building and simulation game; I thought I'd share the progress so I can ask some questions in case I get stuck.

Here's a sample town (read-only at the moment because the UI is far from complete): 

http://misc.blicky.net/c2/?id=1 (desktop only)

Some screenshots..

DQ2tifU.jpg

WwExabK.jpg

yYVuAsB.jpg

Current issues:

  • Shadows are way too 1990's, even though the targetTexture is 4096 in size. It would be nice if the shadowGenerator's matrix would adopt to only include faces that are in the camera frustum. That way shadows would look good from afar and close-up. Solved
  • Road intersections are a pita. The current solution, using CSGs on the road segments (extruded shapes) and some math to create a plane is barely acceptable and rather slow/error-prone. 
  • Performance. While 'idle' FPS is not bad, when adding more structures, the browser will lag because it is using MergeMeshes() to merge the new structures to the existing meshes. I'm currently transitioning to SPS meshes, because they build a lot faster, however are also more restrictive. So my question is: why is MergeMeshes() slower than SPS.buildMesh() and can it be made to match the latters' speed?

Share this post


Link to post
Share on other sites

The mesh you get with MergeMeshes() is a BJS standard mesh.

The mesh you build with the SPS is also a BJS standard mesh.

So both are the same "speed" once they are built.

Only the way to build them and the associated features differ.

Don't forget to dispose the meshes used as models with mergeMesh() (the same with the SPS) to free some memory.

 

The SPS may be faster when building the final mesh because it "knows", before being built, all the model geometries to be used.

Share this post


Link to post
Share on other sites

@ConvergenceAbout shadows:

I use a modified version of BABYLON.DirectionalLight.prototype.setShadowProjectionMatrix
This sets the high detail shadow to a much smaller box shadowMesh near camera with high resolution.

BABYLON.DirectionalLight.prototype.setShadowProjectionMatrix = function (matrix, viewMatrix, renderList) {
   var orthoLeft = Number.MAX_VALUE;
   var orthoRight = Number.MIN_VALUE;
   var orthoTop = Number.MIN_VALUE;
   var orthoBottom = Number.MAX_VALUE;
   var tempVector3 = BABYLON.Vector3.Zero();
   var activeCamera = this.getScene().activeCamera;

   var hasShadowMesh = (typeof this.shadowMesh != 'undefined'); //size shadow Map to this.shadowMesh only (local area)

   // Check extends
   for (var meshIndex = 0; meshIndex < renderList.length; meshIndex++) {
      var mesh = renderList[meshIndex];
      if (hasShadowMesh) { //only check shadowMesh
         mesh = this.shadowMesh;
         meshIndex = renderList.length+1;
      }
      if (!mesh) {
         continue;
      }
      var boundingInfo = mesh.getBoundingInfo();
      if (!boundingInfo) {
         continue;
      }
      var boundingBox = boundingInfo.boundingBox;
      for (var index = 0; index < boundingBox.vectorsWorld.length; index++) {
         BABYLON.Vector3.TransformCoordinatesToRef(boundingBox.vectorsWorld[index], viewMatrix, tempVector3);
         if (tempVector3.x < orthoLeft)
            orthoLeft = tempVector3.x;
         if (tempVector3.y < orthoBottom)
            orthoBottom = tempVector3.y;
         if (tempVector3.x > orthoRight)
            orthoRight = tempVector3.x;
         if (tempVector3.y > orthoTop)
            orthoTop = tempVector3.y;
      }
   }
   var xOffset = orthoRight - orthoLeft;
   var yOffset = orthoTop - orthoBottom;
   BABYLON.Matrix.OrthoOffCenterLHToRef(orthoLeft - xOffset * this.shadowOrthoScale, orthoRight + xOffset * this.shadowOrthoScale, orthoBottom - yOffset * this.shadowOrthoScale, orthoTop + yOffset * this.shadowOrthoScale, -activeCamera.maxZ, activeCamera.maxZ, matrix);
};

As in this thread:

I am very interested in @Sebavan 's workings on cascading shadows, Can I find something about this? :)

I have seen an interesting thing in Godot engine, a shadow map texture with two levels, one with high detail one with large area (in one texture. on the top left white box)
 

godot_shadow.jpg

Share this post


Link to post
Share on other sites
6 hours ago, BitOfGold said:

@ConvergenceAbout shadows:

I use a modified version of BABYLON.DirectionalLight.prototype.setShadowProjectionMatrix
This sets the high detail shadow to a much smaller box shadowMesh near camera with high resolution.

Thanks :) This worked  great. I actually tried to modify this function to do something similar, but I couldn't get it  to work. But yours works great (see pic below). Also its probably faster because it doesn't have to loop through the renderList every frame.

ahbtSQY.jpg

Share this post


Link to post
Share on other sites

Are you freaking kidding me!?? :blink: OMG! This looks so good!!! Damn, that's just plain awesome!!!! How? Just how? I run into performance problems even when if I try to keep things simple... but you got so much details!

I love it, great job! :wub:

Share this post


Link to post
Share on other sites
25 minutes ago, iiceman said:

Are you freaking kidding me!?? :blink: OMG! This looks so good!!! Damn, that's just plain awesome!!!! How? Just how? I run into performance problems even when if I try to keep things simple... but you got so much details!

I love it, great job! :wub:

Thanks :) Haha I know the feeling, I also had major performance problems in my first babylon projects.

But here are a few pointers though that I learned over the months that increased performance a lot for me:

  • MergeMeshes() and/or SPS for static objects is highly superior in performance to createInstance(). If I'd use instances for all the buildings/props/etc, the draw calls would be the same but the FPS would likely be < 10. This is because Babylon by default computes the matrix for every instance for every frame. mesh.computeWorldMatrix() was consistently at the top of the chrome profiler on my other scenes before I figured this out.
  • The first point will also be also have a more drastic impact if you use texture atlases for your meshes, so even different meshes can be combined into one draw call.
  • In case you have many meshes that move or billboard (like the cars and the lens flares in my scene) you will have to take shortcuts like only animating the cars close to the camera. The others are frozen by .freezeWorldMatrix().
  • Don't use .isVisible = false; rather use isEnabled(false).
  • Don't use rays, or only one per frame.
  • Use mesh.isPickable is false for any mesh that doesn't need interaction.
  • Any mesh that is static, use mesh.freezeWorldMatrix();
  • Any material that doesn't change, use material.freeze(); however its glitchy for any mesh that is animating.
  • Sometimes I even remove meshes from the scene.meshes array in case I need them for something (like a parent to other meshes) but they will never be visible nor will they ever move. Saves another mesh babylon will have to iterate over every frame.
  • Use the browsers' profiler to see what functions consume the most time. 

Hope this helps your project(s) :) With all these in place I think I can fill the whole map with more buildings and foliage without running into serious performance issues, but we will see. 

Share this post


Link to post
Share on other sites

Wow, this must be the best JS game I have ever seen. It looks great!

Regarding shadows, some time ago I worked on a project where we used a technique called Trapezoidal Shadow Maps. I think it's a similar idea where you add more details to shadows near the camera, but it adds a distortion (to maximize details near the camera) that you can then reverse in your shader.

I couldn't give you the details of the matrix maths, but there's a good article about the technique.

Share this post


Link to post
Share on other sites
5 hours ago, inteja said:

@Convergence those are some great performance tips. It'd be good if they could be reviewed and added to the documentation, maybe under Tutorials > Optimizing your scene? I'd add them myself but I'm still a newbie to BJS - a little too green to be contributing to documentation.

Thanks, I'm not sure if they are universal, but they did work for me. But yes, the documentation could give .freezeWorldMatrix() and siblings some love :). I also consider myself too green to poke the documentation :D

Share this post


Link to post
Share on other sites

GTX 570. I dont play games, also the people i know dont play games, this forum is the only place i know of weird people playing games. i have more a scientific and artistic interest on programming, just pushing bits.

I know its a demo, looking forward for an optimized version. At least you can not do much for me. Maybe an old school fog of war effect, that saves some render. 

The pictures looks pretty. Good Luck.

Share this post


Link to post
Share on other sites

I was thinking about it. Here are some thoughts, just to make users with a 30bucks pc happy. Its not Doom 3 u can create a screenshot at some point and just moving some elements (like cars) on a *.png plane, would be funny. You, the dev, only know that that is not a 3D model anymore. (Hyperrealistic Painting)  Also diving everything on your map into tiles and push and pop them with user input on different layers, very easy to implement. Also async load of files. I rather wait for a .babylon file to be phrased as not seeing anything at all. 

Share this post


Link to post
Share on other sites
6 hours ago, Nabroski said:

GTX 570. I dont play games, also the people i know dont play games, this forum is the only place i know of weird people playing games. i have more a scientific and artistic interest on programming, just pushing bits.

I know its a demo, looking forward for an optimized version. At least you can not do much for me. Maybe an old school fog of war effect, that saves some render. 

The pictures looks pretty. Good Luck.

 

2 hours ago, Nabroski said:

I was thinking about it. Here are some thoughts, just to make users with a 30bucks pc happy. Its not Doom 3 u can create a screenshot at some point and just moving some elements (like cars) on a *.png plane, would be funny. You, the dev, only know that that is not a 3D model anymore. (Hyperrealistic Painting)  Also diving everything on your map into tiles and push and pop them with user input on different layers, very easy to implement. Also async load of files. I rather wait for a .babylon file to be phrased as not seeing anything at all. 

Thanks :) I'm working on performance now.. If you have a minute, you can try to see if disabling render targets helps (click the FPS meter to open the babylon debug layer, then uncheck render targets). But I'll get a more rigorously optimized version out soon with a settings dialog to disable and enable certain options.

 

Hmm, what FPS are you getting for this link? http://misc.blicky.net/c2/?id=1&highfps

Share this post


Link to post
Share on other sites

GPU : NVidia Quadro K620

CPU : Intel Xeon 4 x 3.10 GHz

OS : linux Ubuntu 16.04 LTS 64b  (desktop)

Browser : Chrome

first demo link = 20 fps

second demo link (highfps) = 58-60 fps

Really really really awesome and impressive !

Congratulations ;)

 

I noticed that you drastically reduced the number of draw calls. What is the main difference under the hood between both the scenes ?

Share this post


Link to post
Share on other sites
35 minutes ago, jerome said:

GPU : NVidia Quadra K620

CPU : Intel Xeon 4 x 3.10 GHz

OS : linux Ubuntu 16.04 LTS 64b  (desktop)

Browser : Chrome

first demo link = 20 fps

second demo link (highfps) = 58-60 fps

Really really really awesome and impressive !

Congratulations ;)

 

I noticed that you drastically reduced the number of draw calls. What is the main difference under the hood between both the scenes ?

Thanks, great :)

Disabling shadows (one of the main differences) will reduce the number of draw calls by half, since almost all meshes are in the renderList of the shadowGenerator. Additionally only reflecting the skybox instead of the city itself helps, and reducing the number of cars, lens flares and props.

If I could ask one more minute of your time, how's the FPS in the second link but with shadows re-enabled (through the babylon debug layer)?

edit: nice CPU btw ;)

Share this post


Link to post
Share on other sites

how can I display the debug layer ?

ok just found it

140 draw calls and 30 fps stable in Chrome

and the DL still visible (what can consume some fps also)

 

and it rises back to 60 fps when the scene becomes darks so maybe no shadow anymore !

 

really nice

Share this post


Link to post
Share on other sites

Just to let you know couldn't to get it to load in firefox 50.0.2 worked brilliantly in Chrome

Console messages

unreachable code after return statement[Learn More]  roads.js:1006:1
TypeError: scene.getMeshByName(...) is null[Learn More]  roads.js:543:12

 

 

 

 

 

Share this post


Link to post
Share on other sites
5 minutes ago, JohnK said:

Just to let you know couldn't to get it to load in firefox 50.0.2 worked brilliantly in Chrome

Console messages

unreachable code after return statement[Learn More]  roads.js:1006:1
TypeError: scene.getMeshByName(...) is null[Learn More]  roads.js:543:12

 

 

 

 

 

Thanks, might just be a network error; I got that error one time also but a refresh worked for me :)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.