Show pageOld revisionsBacklinksBack to top This page is read only. You can view the source, but not change it. Ask your administrator if you think this is wrong. ====== Initial TileSamplerZ implementation ====== /* Started on 08/02/2022 */ * Hey hey hey! So we continue our investigations on the ElevationProducer usage today. And so, the first thing I can note is that in the reference **terrain1/helloworld.xml** file from Proland, we are assigning our ElevationProducer to the **TerrainNode** somehow: <sxh xml; highlight: []> <node name="scene"> <node flags="camera"> <method id="draw" value="cameraMethod"/> </node> <node name="terrainNode" flags="object,dynamic"> <bounds xmin="-50000" xmax="50000" ymin="-50000" ymax="50000" zmin="0" zmax="5000"/> <field id="terrain" value="terrain"/> <tileSamplerZ id="elevation" sampler="elevationSampler" producer="groundElevations1" storeInvisible="false"/> <tileSampler id="fnormal" sampler="fragmentNormalSampler" producer="groundNormals1" storeParent="false" storeInvisible="false"/> <mesh id="grid" value="quad.mesh"/> <method id="update" value="updateTerrainMethod"/> <method id="draw" value="drawTerrainMethod"/> <module id="material" value="terrainShader"/> </node> </node></sxh> * Let's first check what details we can get on that **tileSampleZ** attribute: the ''TileSampleZ'' class is a derived class from ''TileSampler''. * Currently we only have a minimal skeleton implementation for TileSampler in nvland, so let's add both classes now. * => Added correct implementation for TileSampler constructor, and some of the getters. <note>In the TileSample constructor documentation it is reported that the producer parameter may be null in some cases, but that is incorrect: we always try to access the producer data in the constructor so it must be valid.</note> * Argghh... 😖 We are starting to get a pretty large mess of classes, and inner classes, and inheritance, and all... I hope I can manage that till the end :-S (Currently implementing "TileSamplerZTree" class) * Oh my god... Now we also have the **TileSamplerZ::State** class and yet another factory for that lol. Maybe I should stop here for tonight 😅. * Okay, now back to this task with at least some energy to put in it: let's continue on the TileSamplerZState implementation. * => we need to add the method ''FrameBuffer::setTextureBuffer()'': Arrf, no, in fact we already have that as **setTexture2DBuffer()** 😁 * We need to implement **GPUTileStorage.getTexture()**: **OK** * Okay, and next, probably another big chunk: we need the **ReadbackManager** implementation, let's see... * Hhmmmm, this is all interesting: this ReadbackManager class is all about reading back some data from the GPU to the CPU "asynchronously" * Except that, I'm not completely sure this is really asyn yet ? (well it should I guess), it's mostly happening in: <sxh cpp; highlight: []>bool ReadbackManager::readback(ptr<FrameBuffer> fb, int x, int y, int w, int h, TextureFormat f, PixelType t, ptr<Callback> cb) { if (readCount[0] < maxReadbackPerFrame) { int index = readCount[0]; fb->readPixels(x, y, w, h, f, t, Buffer::Parameters(), *(toRead[0][index])); toReadCallbacks[0][index] = cb; ++readCount[0]; return true; } else { assert(false); // should not happen, call canReadback before return false; } }</sxh> * Checking the **FrameBuffer::readPixels()**, this is based on glReadPixels: <sxh cpp; highlight: []>void FrameBuffer::readPixels(int x, int y, int w, int h, TextureFormat f, PixelType t, const Buffer::Parameters &s, const Buffer &dstBuf, bool clamp) { if (Logger::DEBUG_LOGGER != NULL) { Logger::DEBUG_LOGGER->logf("RENDER", "read %d pixels", w * h); } set(); dstBuf.bind(GL_PIXEL_PACK_BUFFER); s.set(); glClampColor(GL_CLAMP_READ_COLOR, clamp ? GL_TRUE : GL_FALSE); glReadPixels(x, y, w, h, getTextureFormat(f), getPixelType(t), dstBuf.data(0)); s.unset(); dstBuf.unbind(GL_PIXEL_PACK_BUFFER); dstBuf.dirty(); assert(getError() == 0); }</sxh> * => Actually found an interesting page the [https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/WebGL_best_practices|WebGL Best practices], also suggesting to read pixels asynchronously. But I need to understand how this works now 🤣 * => Okay, so yes: maybe we could implement the ReadbackManager.readback() method so that it returns a promise. And then we can call the callback we want after that promise ? * So, this bring the question of: do we need the ReadbackManager at all ? is we simply just add the readPixelsAsync to the Framebuffer, then we could pass callback there, so no need for readbackManager and/or call to nextFrame() (?). Let's try that path. * We now have our constructor for the State class, next, we need the factory for it: **OK**, we now have the class **TileSamplerZStateFactory**, we need to "install that" now: this should be done in the init for TileSamplerZ: <sxh typescript; highlight: []> async init() { // Get or create the state factory: let factory = RenderContext.getCurrent().getOrCreateFactory('TileSamplerZState', TileSamplerZStateFactory); // Create our state object: let storage = this.producer.getCache().getStorage().asGPUTileStorage(); this.state = await factory.get(storage); }</sxh> * And finally, i'm adding an helper function to create a TileSamplerZ object async: <sxh typescript; highlight: []>export async function createTileSamplerZ(producer: TileProducer, name: string = null): Promise<TileSamplerZ> { let ts = new TileSamplerZ(producer, name); await ts.init(); return ts; }</sxh> * **Cool!** => Now I should be able to instanciate a TileSamplerZ object 😳.. Well, in theory... Let's try that 😅. * Hmmm, and now I'm just realizing that the name parameter for a TileSampler should really be the GLSL uniform name for the corresponding sampler => using the default "TileSampler" name doesn't make any sense then. * Okay so... not quite working out of the box unfortunately lol... I seem to get a freeze in the process when starting to create the TileSamplerZState. Let's see... puuff, stupid me: we get an infinite loop with that kind of while if not using real integers of course: <sxh typescript; highlight: []> let pass = 0; while (h != 1) { h = h / 4 + (h % 4 == 0 ? 0 : 1); pass += 1; }</sxh> * => Yeepee! And now it seems I'm creating my TileSampleZ object without any further trouble! Good good. * Some final touch now on the init process... => just calling ''tsZ.setStoreInvisible(false)'' and now we are good! => Next time, we will create the **TileSampler** class... or maybe not 🤔: we probably need to start with the NormalProducer first. public/projects/nervland/notes/0004_landjs_initial_tilesamplerz.txt Last modified: 2022/02/09 20:28by 127.0.0.1