Using the Producers in the TerrainNode
- In proland the actual usage of the TileSamplers happens in the drawTerrain method assigned to the terrain node: but from my perspective, there is no real interest to store the TileSamplers separately and then retrieve them in the draw call: we can just as well store them as elements of our TerrainNode (I think…).
- So we add in the TerrainNode class:
// We store the TileSamplers directly in this TerrainNode: protected samplers: TileSampler[] = []; // Add one TileSampler: public addTileSampler(sampler: TileSampler) { this.samplers.push(sampler); }
- And we assing the samplers accordingly in our Test8 Scene setup: OK
- ⇒ back from “vacation mode”: so now checking if everything is still working as needed on main dev server: OK everything seems to be in order.
- Next we have to understand what's happening in the draw() call.
- but first we need to implement a few missing methods:
TileSampler.getTerrain()
: OKTileSampler.checkUniforms()
: OKGPUTileStorage.getTileMap()
: OKGPUTileStorage.generateMipMap()
: OKFrameBuffer.drawQuad()
: OK
- Note: to implement the drawQuad method in FrameBuffer, we added a mechanism directly in the ResourceManager to create a default Quad object:
public getDefaultQuad(): Mesh { if (this.quad == null) { let indexType = AttributeType.UNSIGNED_SHORT; let meshMode = MeshMode.TRIANGLE_STRIP; let meshUsage = MeshUsage.GPU_STATIC; // For each vertex, we pass the 4 position coordinates as float: let nverts = 4; // Size of 1 vertex in bytes: let vertSize = 4 * 4; // create the mesh: this.quad = new Mesh(vertSize, indexType, meshMode, meshUsage, nverts); this.quad.addAttributeType(0, 4, AttributeType.FLOAT, false); this.quad.addVertexElements([-1, -1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, 1, 1, 0, 1]); } return this.quad; }
- Also implemented
TileSampler.setTileMap()
: OK
- Note: One thing that we just changed that is going to break the system already is the addition of the setTexture2DArrayBuffer method in the FrameBuffer class ⇒ now we also need to setup the attachments correctly for Texture2DArray objects.
- Ouughhtt… it seems we may have a serious problem here: when attaching a Texture2DArray with layer==-1, we are calling the opengl function glFramebufferTexture() in proland, but it seems there is nothing corresponding to this in WebGL2 ?
- ⇒ Checking what the mipmapProg shader is doing in GPUTileStorage:
- This is loaded with:
this.mipmapProg = await rman.loadProgram('tileStorageMipMap');
- Note: we are only loading that program if mipmaps are required, which doesn't seem to be the case for now ?
- Arrgh… that shader is using a Geometry shader, and contains a gl_Layer instruction: we can probably not use this as is.
- Note: there is no gl_Layer at all in OpenGL ES 3.0 (cf. https://www.khronos.org/registry/OpenGL-Refpages/es3/html/gl_Layer.xhtml), so we will need to attach the layers we want to render one by one, and using “layer==-1” should be prevented. ⇒ TO BE fixed eventually
- Next issue we have in with the non implemented
TileSampler.setTile()
function, let's fix that: OK - But now we also need the
Tile.getData()
method. - And now things are getting a bit more tricky: because this getData() method is going to check the result of a Tile generation task:
TileStorage::Slot *TileCache::Tile::getData(bool check) { bool isDone = task->isDone(); assert(isDone || !check); assert(getTId() == data->id || !check); return isDone ? data : NULL; }
- hmmm… 🤔 how can we handle that properly ? Arrff… I think the easiest option here is to use a similar implementation with a dedicated “Task” class too.
- So let's write the Task class: OK, now I have an initial Task class. Let's continue with the Tile class.
- Actually, there is still something not quite clear at this level: when we check the getTId() value against the id in the TileStorageSlot: currently I have no idea where that id is written ⇒ to be clarified eventually.
- ⇒ Surprisingly, we have now reached a point with the application will still compile and run properly 😅 So, let's make a commit and then keep messing even further lol.
- What I find strange here, is that we call
setTile()
on the TileSamplers, but there is no location where we actually create a task to “produce that tile”, what am I missing in this process ?
- ⇒ from the proland terrain1 example helloworld.xml file, we see that the TileCache object should be “linked” to the scheduler somehow: maybe this is what I'm missing then ? Checking… (actually, the point is, my
Tile
constructor is never called in fact.)
- Or, in fact, something else interesting here is that in TileSampler.setTile() we will exist the method early if samplerU == null which is the case here since I haven't changed the terrain program yet.
- Ohh, okay: there is definitely a lot missing in my TileCache implementation… for instance
TileCache.getTile
: this should probably be called somewhere… - One step further: it seems this tile creation process is all starting with a call to
TileProducer.startCreateTile
(which I have not implemented yet in my Elevation/normal producers). - Hmm, okay, so in the TerrainNode draw method, we are actually also calling
TileProducer.hasTile
andTileProducer.findTile
at some locations ⇒ So this could be where we start the creation of the tiles ? - And finally, I just found that the “real magic” is probably what is happening in TileSampler.update() 😁! Where do we call that ?
- ⇒ We have the UpdateTileSamplersTask for that.
- Ahh! Eureka: now I have it in the helloworld.xml file:
<sequence name="updateTerrainMethod"> <updateTerrain name="this.terrain"/> <updateTileSamplers name="this.terrain"/> </sequence>
* hmmmm... 🥴 OK, so... I'm not really convinced anymore I can really go somewhere with this implementation of the project: in Javascript we don't have any real/proper support for multithreading: so, whenever I want to update an object on a different thread, I'm in serious trouble. And I'm just realizing that the "TaskGraph" system in use in Proland is doing precisely that all over the place 😳 So how am I doing to cope with that ? Sounds too complex for too limited results.
- And now… I'm starting to think I should give Rust and wgpu a try instead 😭 That is so absolutely crazy and will take me so much more time to rebuild everything again from scratches… Sometimes I really wish I could simply just die instead lol [That would simplify my life a lot! 🤣]
- ⇒ Now the nice thing is, when building an app in Rust, it seems it should then be possible to compile it to web assembly (mostly?) and then run it in a webbrowser… ? I'm not quite sure about all this. But just using plain Javascript/Typescript doesn't seem to be the way to go anyway.
- ⇒ So let's give Rust a try now!