Quad tree terrain LOD engine to Playcanvas integration

A chunk-based quad tree terrain LOD engine (based off Thatcher Ulrich’s code and my own added stuff/implementations done back then in 2013 for Flash) which i’ve ported over to PlayCanvas. Currently can preview the LOD builds in effect in the PLAY preview. Apparently, my approach to the terrain LOD geometry turns out pretty similar to how Battlefield 2 does it as well:
http://www.dice.se/wp-content/uploads/2014/12/Chapter5-Andersson-Terrain_Rendering_in_Frostbite.pdf#page=15 based off Section 5.4 , which is basically a quad tree of 33x33 vertex chunk leafs and 9 permutation index buffers for stiching neighboring LOD levels between them.

Playcanvas project link

Features already integrated:

  • Optimized Front to Back rendering order of quad tree terrain chunk nodes. (so it minimizes overdraw as well)
  • Terrain LOD quad tree updates when camera changes position . (with adjustable ‘detail’ level setting)
  • Highly performant hierachical Frustum culling for terrain chunks in the quad tree.
  • Highly performant 100% accruate straight-line ray-casting within Quad Tree and DDA terrain chunk grid. (with nearest hit early out exit approach) . Suited for extremely long distance rays hit time detections.
    (In the demo you can see the white box being traced over the terrain for camera’s center forward ray)
  • Some splat mapping/terrain-texturing integrations.
  • Highly performant basic ellipsoid character movement collision detection/clipping/movement on Quad Tree and terrain chunk grid using a cross-platform “basic player collision clipping to 3D environment” package. (note: unlike the raycaster, this is mainly optimized for short distanced movement per frame only)

On the roadmap are the following features to integrate properly into PlayCanvas’ 3D environment.
Features to integrate into Playcanvas:

  • Highly performant 100% accruate trajectory (quadratic curve ) raycasting within Quad Tree and DDA terrain chunk grid. with nearest hit early out exit approach). Suited for extremely long distance trajectory hit time detections.
    I use this to batch pre-calculate stopping locations for a shader (from another engine) that renders hundreds (or even thousands) of bullet-debris bounce-off particles (or archers’ arrows) that shoot them out across the terrain geometry (ie. the particles remain visibly resting on the ground terrain due to shader inputs)

On the roadmap for terrain engine / Playcanvas:

  • Grid-based quadtree grid network to allow for infinitely larger environments.
  • Streaming/loading/unloading of terrain LOD quadtree assets in quadtree grid network for even larger worlds to go along with LRU pooling approach instead of hard cache. (I already have doubly linked list of chunk states to facilitate this).

Could optimize (Playcanvas specific):

  • Memory VRAM optimization…eg. How do i maintain and re-use/share the same index buffer per model chunk being created? Currently, it seems pc.createMesh always causes/requires a new index buffer to be created, which is not what i want!! (ie. how do i reuse an existing index buffer i have upon creating a new mesh?)
  • Also, come up with support for Materials without requiring Tangents/Normals to be supplied, to save on VRAM vertex buffer usage.
  • Also consider VTF instead of buffering/pooling vertex buffer chunks.

Rough Polycount stats:

  • Medium sized terrain (512x512 tiles), can go up to around draw chunk calls around the 50-ish range at worse case (out of original 256 calls). (for around 32*32*2*50=102400 triangles). Works great on mobile.
  • Large sized terrain. (1024x1024 tiles). Also works great in performance draw counts at double only for worse case. Not too sure how it works on mobile because the current unoptimized vertex/index buffers approach to integrate conveniently to Playcanvas crashes browser on mobile atm though (eg. IOS, iphone 5/6, etc.). Anyway, 1024x1024 may be considered too unnecessarily big in some cases as well.

‘Good to have’ Future terrain texturing implementations/utility:

  • Paint live-terrain-editing splatting utilities and saving
  • Paint live-terrain-editing texture tiling (+ shader) and saving. (my own tile-based texture atlas shader)

Terrain Branches/Forks:


Actually, would like to make correction for:

  • Optimized Front to Back rendering order of quad tree terrain chunk nodes. (so it minimizes overdraw as well)

This feature hasn’t been integrated yet actually, particularly due to the new Layer system in Playcanvas and such. BUt it shouldnt be too much of a problem as the codebase already knows the optimal draw order to draw terrain chunks (since it’s a quadtree). The LODing alone keeps things fairly optimized already, and this additional feature of determining draw orders to be more optimal (front-to-back chunk rendering) within Playcanvas draw call list within Layers, (and also optimal use of meshInstances vs entities, etc.) would require some digging into the engine code which hasn’t been done yet. It’s one of those YAGNI optimization features that is low priority imo.


hi every people around da world !

@Glenn_Ko pretty nice , run just perfect by my side !
i land here by searching on forum on how to move my entite on a non flat land & how it could easily follow reliefs ( and so have easy import of any land, or as you, if got it, generated land ).

i looked at the playerMovement.js but still confused (am quite new on paycanvas engine), sounds like :

> force.set (x, 0, z).normalize ().scale (this.speed);
> this.entity.translate (force);
> this.entity.rigidbody.syncEntityToBody ();

make the job or anything u had to code deeper ? tried somes here but didnt succed

am using point&click & orbit-camera scripts from tutorial atm, i only have something like

> var result = this.groundShape.intersectsRay(ray, hitPosition);

Shall i try something like intersectsRay(myland, hitPosition) ? where can i get the height of the land from screentoworld method or any ?

Shall i look physics & hitbox ?

thx for somes advices ^^

ps : here my current study

No longer using playerMovememt.js. I’m not using Playcanvas physics. Check the scripts under the CharacterDemo entity. Those are using custom collision codes from altern codebase ( which included terrain engine core code) exported out from haxe. Ellipsoid Collision/movement and Raycasting queries are made with altern.EllipsoidCollider and altern.Raycaster respectively.

The collision scene engine is based Fast 3D raycasting/collision Library / Scripts preview If the size of the world is just that (like in the example you gave)… a regular Model/mesh would be better. In fact, i think you are already using a static mesh (a combination of models). In which case, use the examples found in the link above instead.

Regarding TerrainLOD and collision engine, however, i haven’t hooked it up to also recognise nodes with terrainLOD script in order to link collision to those terrain as well (the core altern codebase actually supports this so it’s only a matter of scripting within Playcanvas). Need to come up with an official example that integrates parsing combined collision graph from playcanvas scene graph + terrainLODs/other subtrees nodes support (manage multiple terrainLODs as part of a much larger world as well…). Also need to see what sort of development/level-design workflow would work best as well when it comes to manually placing objects on such large LOD terrains within/outside editor.

thx for quick & helpfull answer ^^.

i was actually trying to raycast with the shape-land i have but sounds like i need more docs to read (quite new here in playcanvas) as it’s an entity but intersectsRay dont want to be a function on such a mesh (sounds only like boundingsphere & boundingbox does).

i am going to take a look to ur library as what i want is an easy mesh importing & keep it stupid simple as possible to give me “students” (teenages & me own child) a chance (a same for me :D)