====== Using the Producers in the TerrainNode ====== /* Started on 19/02/2022 */ * 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()'': **OK** * ''TileSampler.checkUniforms()'': **OK** * ''GPUTileStorage.getTileMap()'': **OK** * ''GPUTileStorage.generateMipMap()'': **OK** * ''FrameBuffer.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'' and ''TileProducer.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: * 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!