breakds Posted October 19, 2017 Share Posted October 19, 2017 Hi Almighty Babylon Community, I am new to Babylon (and JavaScript in general), and am working on rendering huge tiled maps. The tiled map consists of many square cells are each cell is a 2048 x 2048 pngs (served by HTTP from the server as static files). At each moment we only need to render ~ 12 cells covering an area centered at the main entity. As the main entity moves, it might need to render another set of cells, although the new set of cells shares most part with the old set. My current approach is to render the set of cells with a TiledGround. The TiledGround has MultiMaterial attached to it, and each material in the MultiMaterial has a texture with the corresponding png. Each time when the main entity moves to the point that we need to update the TiledGround, the original TiledGround is disposed and a new TiledGround is created. The problem is that, I can feel the lagging at the moment when the disposing/recreation happens. I am wondering: Since the new set of cells and the old set of cells share most of cells, is there a way to incrementally update the TiledGround so that the lagging can go away? I am setting `.isblocking = false` for all the textures hoping that this can help with the lagging (it is okay to display a black cell first and have texture filled in later when it is loaded). Is this the right way to achieve the goal? Would using multiple Grounds instead TiledGround help? I am worrying that this might not the direction to go. Thanks a lot. Below is the code I have for this task, where `syncState` is called every time when an update on the tile map happens. I would like to do this on the PG but I am not sure how I can have it interact with the cell images, sorry! Thanks a lot for your time and patience import * as BABYLON from "babylonjs"; import { onSnapshot } from "mobx-state-tree"; import STORE from "store"; /** Maintains the 3D entity in BABYLON for tile maps. **/ export default class TileMap { constructor(scene) { this.scene = scene; // Under the hood, the tile map is represented as a tiled ground, // with a multi material. this.tiledGround = null; this.multiMaterial = null; // Create the pure black material for tiles without an image. this.pureBlack = new BABYLON.StandardMaterial("PureBlack", this.scene); this.pureBlack.diffuseColor = new BABYLON.Color3.Black(); // Connects the global state so that this becomes a view of the // the tileMap state. onSnapshot(STORE.tileMap, state => { if (!state.pending) { this.syncState(STORE.tileMap); } }); } /** * Updates the geometric properties of the mesh to make it in sync with the * given state. */ syncState(state) { if (this.tiledGround) { this.tiledGround.dispose(); } // 1. Prepare the multi material for the updated tiled ground. this.multiMaterial = new BABYLON.MultiMaterial( "TileMapMaterial", this.scene); for (let v = 0; v < state.rows; ++v) { for (let u = 0; u < state.cols; ++u) { const i = (state.rows - v - 1) * state.cols + u; if (state.tile[i]) { const image = new BABYLON.StandardMaterial(state.tile[i], this.scene); image.emissiveTexture = new BABYLON.Texture(state.tile[i], this.scene); image.emissiveTexture.uscale = 1; // Invert the v (Y) axis because the tile map has inverted Y. image.emissiveTexture.vscale = -1; // Asnchronously load the image texture. image.emissiveTexture.isBlocking = false; this.multiMaterial.subMaterials.push(image); } else { this.multiMaterial.subMaterials.push(this.pureBlack); } } } // 2. Create the tiled ground. this.tiledGround = new BABYLON.Mesh.CreateTiledGround( "TileMap", state.origin.x, -(state.origin.y + state.height), state.origin.x + state.width, -state.origin.y, { h: state.rows, w: state.cols }, // Number of tiles along x and z (Y). { h: 2, w: 2 }, // Precisions for each tile. this.scene); // 3. Apply the multi material to the tiled ground. this.tiledGround.material = this.multiMaterial; const numTiles = state.rows * state.cols; const verticesCount = this.tiledGround.getTotalVertices(); const tileIndicesLength = this.tiledGround.getIndices().length / numTiles; // Distribute the multi material into sub meshes. this.tiledGround.subMeshes = []; for (let i = 0; i < numTiles; ++i) { this.tiledGround.subMeshes.push(new BABYLON.SubMesh( i, 0, verticesCount, tileIndicesLength * i, tileIndicesLength, this.tiledGround)); } } } Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted October 19, 2017 Share Posted October 19, 2017 Hello and welcome! 1. Yes you can. You can provide the current mesh to the new TiledGRound constructor (last parameter). This way the new TiledGround will just update the old one and no instantiation will be required 2. Definitely! 3. Funnily it was the first idea that came to my mind when I started reading your topic. If 1. here does not work then it is clearly something you can try From a more general standpoint: JavaScript only support one thread. Which means that heavy operation like creating a texture can block the rendering for a long time. Enough to notice laggyness. With that in mind, it will always be faster to update a resource already created than creating a new one Quote Link to comment Share on other sites More sharing options...
breakds Posted October 19, 2017 Author Share Posted October 19, 2017 Thanks a lot for the prompt reply, Deltakosh! These advises are very helpful! About the the first comment on the TiledGround constructor, I am not sure whether my understanding is correct. To achieve that I think what I should do is to set `canBeRegenerated` to true, and provide the last parameter (previous TiledGround mesh), is this correct? I understand this can avoid instantiation again, but since after constructing the TiledGround, the new materials are applied and new sub-meshes are attached, what happens to the previous materials/textures/sub-meshes? Do they get disposed automatically? Sorry for asking question about the basics. Thanks again! Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted October 20, 2017 Share Posted October 20, 2017 you can still keep track of old one and dispose them Quote Link to comment Share on other sites More sharing options...
breakds Posted October 22, 2017 Author Share Posted October 22, 2017 I see. Thanks. I will manually dispose them then! Quote Link to comment Share on other sites More sharing options...
amethlex Posted October 26, 2017 Share Posted October 26, 2017 On 10/19/2017 at 9:18 AM, Deltakosh said: Hello and welcome! 1. Yes you can. You can provide the current mesh to the new TiledGRound constructor (last parameter). This way the new TiledGround will just update the old one and no instantiation will be required 2. Definitely! 3. Funnily it was the first idea that came to my mind when I started reading your topic. If 1. here does not work then it is clearly something you can try From a more general standpoint: JavaScript only support one thread. Which means that heavy operation like creating a texture can block the rendering for a long time. Enough to notice laggyness. With that in mind, it will always be faster to update a resource already created than creating a new one Hi Deltakosh, I have met the identical problem as breakds. I have checked the document(http://doc.babylonjs.com/how_to/set_shapes#tiled-ground) and the the source code of CreateTiledGround and cannot find input parameter as current mesh. Can I just update the parameters of tiledGround directly to reuse tiledGround? e.g this.tiledGround = new BABYLON.Mesh.CreateTiledGround(...) ... // Updates the parameter. this.tiledGround.xmin = 5 Thank you. Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted October 26, 2017 Share Posted October 26, 2017 Check last parameter: http://doc.babylonjs.com/classes/3.1/tiledground#new-tiledground-classes-3-1-tiledground-id-scene-xmin-zmin-xmax-zmax-subdivisions-precision-canberegenerated-mesh- You will just need to update the paramters like you did and call the constructor again Quote Link to comment Share on other sites More sharing options...
amethlex Posted October 26, 2017 Share Posted October 26, 2017 1 hour ago, Deltakosh said: Check last parameter: http://doc.babylonjs.com/classes/3.1/tiledground#new-tiledground-classes-3-1-tiledground-id-scene-xmin-zmin-xmax-zmax-subdivisions-precision-canberegenerated-mesh- You will just need to update the paramters like you did and call the constructor again Hi Deltakosh, Do you mean I do it like // Creating this.tiledGround = new BABYLON.Mesh.CreateTiledGround(id, xmin, zmin, xmax, zmax, subdivisons, precision, true); ... ... // Updating this.tiledGround.xmin = new_xmin; this.tiledGround.zmin = new_zmin; this.tiledGround.xmax = new_xmax; this.tiledGround.zmax = new_zmax; this.tiledGround.subdivisions = new_subdivisions; this.tiledGround = new BABYLON.Mesh.CreateTiledGround(); Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted October 26, 2017 Share Posted October 26, 2017 Almost // Creating this.tiledGround = new BABYLON.Mesh.CreateTiledGround(id, xmin, zmin, xmax, zmax, subdivisons, precision, true); ... ... // Updating this.tiledGround = new BABYLON.Mesh.CreateTiledGround(id, new_xmin, new_zmin, new_xmax, new_zmax, new_subdivisions, precision, true, this.tiledGround); Quote Link to comment Share on other sites More sharing options...
amethlex Posted October 26, 2017 Share Posted October 26, 2017 3 minutes ago, Deltakosh said: Almost // Creating this.tiledGround = new BABYLON.Mesh.CreateTiledGround(id, xmin, zmin, xmax, zmax, subdivisons, precision, true); ... ... // Updating this.tiledGround = new BABYLON.Mesh.CreateTiledGround(id, new_xmin, new_zmin, new_xmax, new_zmax, new_subdivisions, precision, true, this.tiledGround); Thank you so much Deltakosh! I will try it. Quote Link to comment Share on other sites More sharing options...
amethlex Posted October 26, 2017 Share Posted October 26, 2017 21 minutes ago, Deltakosh said: Almost // Creating this.tiledGround = new BABYLON.Mesh.CreateTiledGround(id, xmin, zmin, xmax, zmax, subdivisons, precision, true); ... ... // Updating this.tiledGround = new BABYLON.Mesh.CreateTiledGround(id, new_xmin, new_zmin, new_xmax, new_zmax, new_subdivisions, precision, true, this.tiledGround); Hi Deltakosh, I am current using tiledground to display a map. And I would like to only display the surrounding map of a moving object, say, a car. So I would like to redraw the submeshes of this tiledground frequently. I do it in this way: while (...){ this.tiledGround.subMeshes = []; // push new submeshes. for (...) { this.tiredGround.subMeshes.push(new BABYLON.SubMesh(...)) } } But I find the previous submeshes are still there! So my question is that how to dispose previous submeshes? Thank you! Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted October 27, 2017 Share Posted October 27, 2017 This should work..I will need a repro in the PG to help more Quote Link to comment Share on other sites More sharing options...
amethlex Posted October 30, 2017 Share Posted October 30, 2017 Hi Deltakosh, Thank you so much. I turned to use multiple grounds instead of tiledground. GameMonetize 1 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.