From 48544dc3d31f26f23a9f841a0eee51bbd7ef4730 Mon Sep 17 00:00:00 2001
From: Matevz Tadel <mtadel@ucsd.edu>
Date: Mon, 21 Dec 2020 14:39:25 -0800
Subject: [PATCH] Add picking to RenderCore.

*  ui5/eve7/lib/GlViewerRCore.js
   - Picking for RCore.
   - Somewhat better RCore bootstrap, need real module loader with RCore+Reve stuff.
   - Enable EXT_color_buffer_float.

* ui5/eve7/lib/RendeQuTor.js
  - Add picking render pass, should really use reduced image and
    small region around camera.
  - Depth extraction -- partial.

* ui5/eve7/lib/EveElementsRCore.js
  Picking title.

* ui5/eve7/shaders/programs.json
  Register copyDepth2RReve shaders.

* ui5/eve7/shaders/custom/copyDepth2RReve.frag
* ui5/eve7/shaders/custom/copyDepth2RReve.vert
  New files: Copy depth buffer to r32f texture.
---
 ui5/eve7/lib/EveElementsRCore.js             |   9 +-
 ui5/eve7/lib/GlViewerRCore.js                | 122 ++++++++++---------
 ui5/eve7/lib/RendeQuTor.js                   | 115 +++++++++++++++--
 ui5/eve7/shaders/custom/copyDepth2RReve.frag |  36 ++++++
 ui5/eve7/shaders/custom/copyDepth2RReve.vert |  20 +++
 ui5/eve7/shaders/programs.json               |  18 ++-
 6 files changed, 249 insertions(+), 71 deletions(-)
 create mode 100644 ui5/eve7/shaders/custom/copyDepth2RReve.frag
 create mode 100644 ui5/eve7/shaders/custom/copyDepth2RReve.vert

diff --git a/ui5/eve7/lib/EveElementsRCore.js b/ui5/eve7/lib/EveElementsRCore.js
index 1758577f4be..4567884a167 100644
--- a/ui5/eve7/lib/EveElementsRCore.js
+++ b/ui5/eve7/lib/EveElementsRCore.js
@@ -29,6 +29,12 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function (EveManager)
 
    EveElemControl.prototype.separateDraw = false;
 
+   EveElemControl.prototype.getTooltipText = function(intersect)
+   {
+      let el = this.obj3d.eve_el;
+      return el.fTitle || el.fName || "";
+   }
+
    EveElemControl.prototype.elementHighlighted = function (indx)
    {
       // default is simple selection, we ignore the indx
@@ -112,9 +118,10 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function (EveManager)
       return mat;
    }
 
-   EveElements.prototype.RcPickable = function (el, obj3d)
+   EveElements.prototype.RcPickable = function (el, obj3d, ctrl_class=EveElemControl)
    {
       if (el.fPickable) {
+         obj3d.get_ctrl = function() { return new ctrl_class(obj3d); }
          obj3d.colorID = el.fElementId;
          // console.log("YES Pickable for", el.fElementId, el.fName)
          return true;
diff --git a/ui5/eve7/lib/GlViewerRCore.js b/ui5/eve7/lib/GlViewerRCore.js
index b0091b53fb2..dc49a6d5558 100644
--- a/ui5/eve7/lib/GlViewerRCore.js
+++ b/ui5/eve7/lib/GlViewerRCore.js
@@ -51,24 +51,24 @@ sap.ui.define([
          // // console.log(window.location.pathname); // where are we loading from?
          // import("https://desire.physics.ucsd.edu/matevz/alja.github.io/rootui5/eve7/rnr_core/RenderCore.js").then((module) => {
 
-            import("../../eve7/rnr_core/RenderCore.js").then((module) => {
-               console.log("GlViewerRCore.onInit - RenderCore.js loaded");
+         import("../../eve7/rnr_core/RenderCore.js").then((module) => {
+            console.log("GlViewerRCore.onInit - RenderCore.js loaded");
 
-               RC = module;
-               if (this.UseRenderQueue)
-               {
-                  import("../../eve7/lib/RendeQuTor.js").then((module) => {
-                     console.log("GlViewerRCore.onInit - RenderPassesRCore.js loaded");
+            RC = module;
+            if (this.UseRenderQueue)
+            {
+               import("../../eve7/lib/RendeQuTor.js").then((module) => {
+                  console.log("GlViewerRCore.onInit - RenderPassesRCore.js loaded");
 
-                     RP = module;
-                     RendeQuTor = RP.RendeQuTor;
+                  RP = module;
+                  RendeQuTor = RP.RendeQuTor;
 
-                     pthis.bootstrap();
-                  });
-               } else {
                   pthis.bootstrap();
-               }
-            });
+               });
+            } else {
+               pthis.bootstrap();
+            }
+         });
       },
 
       bootstrap: function()
@@ -117,10 +117,16 @@ sap.ui.define([
          this.canvas.width  = w;
          this.canvas.height = h;
 
+         // Enable EXT_color_buffer_float for picking depth extraction
+         // let gl = this.canvas.getContext("webgl2");
+         // let ex = gl.getExtension("EXT_color_buffer_float");
+         // console.log("Create RCore, gl, float_color_buff:", gl, ex);
+
          this.renderer = new RC.MeshRenderer(this.canvas, RC.WEBGL2, {antialias: false, stencil: true});
          this.renderer.clearColor = "#FFFFFFFF";
          this.renderer.addShaderLoaderUrls("rootui5sys/eve7/rnr_core/shaders");
          this.renderer.addShaderLoaderUrls("rootui5sys/eve7/shaders");
+         this.renderer.pickDoNotRender = true;
 
          this.scene = new RC.Scene();
 
@@ -171,12 +177,12 @@ sap.ui.define([
          if (this.controller.kind === "3D")
          {
             /*
-              let c = new RC.Cube(100, new RC.Color(1,.6,.2));
-              c.material = new RC.MeshPhongMaterial();
-              c.material.transparent = true;
-              c.material.opacity = 0.5;
-              c.material.depthWrite  = false;
-              this.scene.add(c);
+            let c = new RC.Cube(100, new RC.Color(1,.6,.2));
+            c.material = new RC.MeshPhongMaterial();
+            c.material.transparent = true;
+            c.material.opacity = 0.5;
+            c.material.depthWrite  = false;
+            this.scene.add(c);
             */
             let ss = new RC.Stripe([0,0,0, 100,50,50, 100,200,200]);
             ss.material.lineWidth = 20.0;
@@ -184,7 +190,7 @@ sap.ui.define([
             this.scene.add(ss);
          }
 
-         this.rot_center = new THREE.Vector3(0,0,0);
+         this.rot_center = new RC.Vector3(0,0,0);
 
          if (this.UseRenderQueue)
          {
@@ -210,9 +216,15 @@ sap.ui.define([
       setupRCoreDomAndEventHandlers: function()
       {
          let dome = this.get_view().getDomRef();
-
          dome.appendChild(this.canvas);
 
+         // Setup tooltip
+         this.ttip = document.createElement('div');
+         this.ttip.setAttribute('class', 'eve_tooltip');
+         this.ttip_text = document.createElement('div');
+         this.ttip.appendChild(this.ttip_text);
+         dome.appendChild(this.ttip);
+
          this.controls = new RC.ReveCameraControls(this.camera, this.get_view().getDomRef());
 
          this.controls.addEventListener('change', this.render.bind(this));
@@ -415,15 +427,23 @@ sap.ui.define([
             this.rqt.render();
          else
             this.renderer.render( this.scene, this.camera );
+
+         // if (this.controller.kind === "3D")
+         //    window.requestAnimationFrame(this.render.bind(this));
       },
 
-      render_for_picking: function()
+      render_for_picking: function(x, y)
       {
-         // console.log("RENDER FOR PICKING", this.scene, this.camera, this.canvas, this.renderer);
+         console.log("RENDER FOR PICKING", this.scene, this.camera, this.canvas, this.renderer);
+         var o3d;
 
-         this.renderer.render( this.scene, this.camera );
+         this.renderer.pick(x, y, function(id) { o3d = id; } );
+         this.rqt.pick(this.scene, this.camera);
 
-         // this.renderQueue.render();
+         // Render to FBO or texture would work.
+         // let d   = pthis.renderer.pickedDepth;
+         console.log("pick result", o3d /* , d */);
+         return o3d;
       },
 
       //==============================================================================
@@ -442,12 +462,7 @@ sap.ui.define([
 
          this.renderer.updateViewport(w, h);
 
-         if (this.UseRenderQueue)
-         {
-            this.rqt.updateViewport(w, h);
-         }
-
-         //this.composer.reset();
+         if (this.UseRenderQueue) this.rqt.updateViewport(w, h);
 
          this.controls.update();
          this.render();
@@ -485,26 +500,21 @@ sap.ui.define([
       /** Get three.js intersect object at specified mouse position */
       getIntersectAt: function(x, y)
       {
-         let w = this.get_width();
-         let h = this.get_height();
-
-         console.log("GLC::onMouseMoveTimeout", this, event, x, y);
-
-         var pthis = this;
-         this.renderer.pick(x, y, function(id)
-                            {
-                               let obj = pthis.get_manager().GetElement(id);
-                               // As things are now, depth can not be known.
-                               // Render to FBO or texture would work.
-                               // let d   = pthis.renderer.pickedDepth;
-                               console.log("pick result", id, obj /* , d */);
-                            }
-                           );
-         this.render_for_picking();
+         console.log("GLC::onMouseMoveTimeout", x, y);
 
+         let o3d = this.render_for_picking(x, y);
+         if (o3d)
+         {
+            let w = this.get_width();
+            let h = this.get_height();
+            let mouse = new RC.Vector2( ((x + 0.5) / w) * 2 - 1, -((y + 0.5) / h) * 2 + 1 );
+            return { object: o3d, mouse: mouse, w: w, h: h };
+         }
+         else
+            return null;
 
          /*
-         let mouse = new THREE.Vector2( ((x + 0.5) / w) * 2 - 1, -((y + 0.5) / h) * 2 + 1 );
+         let mouse = new RC.Vector2( ((x + 0.5) / w) * 2 - 1, -((y + 0.5) / h) * 2 + 1 );
 
          this.raycaster.setFromCamera(mouse, this.camera);
 
@@ -529,16 +539,16 @@ sap.ui.define([
       {
          delete this.mousemove_timeout;
 
-         var intersect = this.getIntersectAt(x,y);
+         let intersect = this.getIntersectAt(x,y);
 
-         if (!intersect)
+         if ( ! intersect)
             return this.clearHighlight();
 
          var c = intersect.object.get_ctrl();
 
-         var mouse = intersect.mouse;
+         let mouse = intersect.mouse;
 
-         c.elementHighlighted(c.extractIndex(intersect));
+         // c.elementHighlighted(c.extractIndex(intersect));
 
          this.highlighted_scene = c.obj3d.scene;
 
@@ -617,10 +627,10 @@ sap.ui.define([
 
          if (intersect) {
             if (intersect.object.eve_el)
-               menu.add("Browse to " + (intersect.object.eve_el.fName || "element"), intersect.object.eve_el.fElementId, this.controller.invokeBrowseOf.bind(this.controller));
+            menu.add("Browse to " + (intersect.object.eve_el.fName || "element"), intersect.object.eve_el.fElementId, this.controller.invokeBrowseOf.bind(this.controller));
          }
 
-         menu.add("Reset camera", this.resetThreejsRenderer);
+         menu.add("Reset camera", this.resetRenderer);
 
          menu.add("separator");
 
@@ -658,4 +668,4 @@ sap.ui.define([
    });
 
    return GlViewerRCore;
-});
\ No newline at end of file
+});
diff --git a/ui5/eve7/lib/RendeQuTor.js b/ui5/eve7/lib/RendeQuTor.js
index 774130fbd4a..fcde8a65e0c 100644
--- a/ui5/eve7/lib/RendeQuTor.js
+++ b/ui5/eve7/lib/RendeQuTor.js
@@ -21,11 +21,18 @@ export class RendeQuTor
         this.scene    = scene;
         this.camera   = camera;
         this.queue    = new RC.RenderQueue(renderer);
+        this.pqueue   = new RC.RenderQueue(renderer);
+        this.make_PRP_plain();
+
+        // Depth extraction somewhat works , get float but in some unknown coordinates :)
+        // If you enable this, also enable EXT_color_buffer_float in GlViewerRCore.createRCoreRenderer
+        // this.make_PRP_depth2r();
+        // See also comments in shaders/custom/copyDepth2RReve.frag
 
         this.SSAA_value = 1;
 
         const nearPlane = 0.0625; // XXXX - pass to view_setup(vport, nfclip)
-        const farPlane = 8192;    // XXXX
+        const farPlane  = 8192;   // XXXX
 
         // Why object.pickable === false in Initialize functions ???
         // How is outline supposed to work ???
@@ -72,6 +79,11 @@ export class RendeQuTor
         {
             rq[i].view_setup(vp);
         }
+        rq = this.pqueue._renderQueue;
+        for (let i = 0; i < rq.length; i++)
+        {
+            rq[i].view_setup(vp);
+        }
     }
 
     render()
@@ -79,6 +91,87 @@ export class RendeQuTor
         this.queue.render();
     }
 
+    pick()
+    {
+        let foo = this.pqueue.render();
+        console.log(foo);
+
+        {
+            let glman  = this.renderer.glManager;
+            let gl     = this.renderer.gl;
+            let texref = this.pqueue._textureMap["depthr32f_picking"];
+            let tex    = glman.getTexture(texref);
+
+            console.log("Dumper:", glman, gl, texref, tex);
+
+            const fb = gl.createFramebuffer();
+			gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+			gl.framebufferTexture2D(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
+
+            let x = this.renderer._pickCoordinateX;
+            let y = this.renderer._canvas.height - this.renderer._pickCoordinateY;
+            console.log(x, y);
+
+            let d = new Float32Array(9);
+            gl.readPixels(x-1, y-1, 3, 3, gl.RED, gl.FLOAT, d);
+            console.log("Pick depth at", x, ",", y, ":", d);
+            /*
+            let d = new Uint32Array(9);
+            gl.readPixels(x-1, y-1, 3, 3, gl.RED, gl.UNSIGNED_INT, d);
+            console.log("Pick depth:", d;
+            */
+
+            gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null);
+            gl.deleteFramebuffer(fb);
+        }
+    }
+
+
+    //=============================================================================
+    // Picking RenderPasses
+    //=============================================================================
+
+    make_PRP_plain()
+    {
+        var pthis = this;
+
+        this.PRP_plain = new RC.RenderPass(
+            RC.RenderPass.BASIC,
+            function (textureMap, additionalData) {},
+            function (textureMap, additionalData) { return { scene: pthis.scene, camera: pthis.camera }; },
+            RC.RenderPass.TEXTURE,
+            null,
+            "depth_picking",
+            [ { id: "color_picking", textureConfig: RC.RenderPass.DEFAULT_RGBA_TEXTURE_CONFIG } ]
+        );
+        this.PRP_plain.view_setup = function (vport) { this.viewport = vport; };
+
+        this.pqueue.pushRenderPass(this.PRP_plain);
+    }
+
+    make_PRP_depth2r()
+    {
+        this.PRP_depth2r_mat = new RC.CustomShaderMaterial("copyDepth2RReve");
+        this.PRP_depth2r_mat.lights = false;
+        var pthis = this;
+
+        this.PRP_depth2r = new RC.RenderPass(
+            RC.RenderPass.POSTPROCESS,
+            function (textureMap, additionalData) {},
+            function (textureMap, additionalData) {
+                return { material: pthis.PRP_depth2r_mat, textures: [ textureMap["depth_picking"] ] };
+            },
+            RC.RenderPass.TEXTURE,
+            null,
+            null,
+            [ { id: "depthr32f_picking", textureConfig: RC.RenderPass.FULL_FLOAT_R32F_TEXTURE_CONFIG } ]
+            // [ { id: "depthr32f_picking", textureConfig: RC.RenderPass.DEFAULT_R32UI_TEXTURE_CONFIG } ]
+        );
+        this.PRP_depth2r.view_setup = function (vport) { this.viewport = vport; };
+
+        this.pqueue.pushRenderPass(this.PRP_depth2r);
+    }
+
     //=============================================================================
 
     make_RP_DirectToScreen()
@@ -90,7 +183,7 @@ export class RendeQuTor
             function (textureMap, additionalData) {},
             function (textureMap, additionalData) { return { scene: pthis.scene, camera: pthis.camera }; },
             RC.RenderPass.SCREEN,
-            null,
+            null
         );
         this.RP_DirectToScreen.view_setup = function (vport) { this.viewport = vport; };
 
@@ -143,7 +236,7 @@ export class RendeQuTor
             // Bind depth texture to this ID
             "depthDefaultDefaultMaterials",
 
-            [ {id: "color_ssaa_super", textureConfig: RC.RenderPass.DEFAULT_RGBA_TEXTURE_CONFIG} ]
+            [ { id: "color_ssaa_super", textureConfig: RC.RenderPass.DEFAULT_RGBA_TEXTURE_CONFIG } ]
         );
         this.RP_SSAA_Super.view_setup = function (vport) { this.viewport = { width: vport.width*pthis.SSAA_value, height: vport.height*pthis.SSAA_value }; };
 
@@ -165,7 +258,7 @@ export class RendeQuTor
 
             // Preprocess function
             function (textureMap, additionalData) {
-                return { material: pthis.RP_SSAA_Down_mat, textures: [textureMap[this.input_texture]] };
+                return { material: pthis.RP_SSAA_Down_mat, textures: [textureMap[pthis.input_texture]] };
             },
 
             // Target
@@ -179,7 +272,7 @@ export class RendeQuTor
 
             [ { id: "color_ssaa_down", textureConfig: RC.RenderPass.DEFAULT_RGBA_TEXTURE_CONFIG } ]
         );
-        this.RP_SSAA_Down.input_texture = "color_ssaa_down";
+        this.RP_SSAA_Down.input_texture = "color_ssaa_super";
         this.RP_SSAA_Down.view_setup = function(vport) { this.viewport = vport; };
 
         this.queue.pushRenderPass(this.RP_SSAA_Down);
@@ -197,10 +290,10 @@ export class RendeQuTor
             RC.RenderPass.POSTPROCESS,
             function (textureMap, additionalData) {},
             function (textureMap, additionalData) {
-                return { material: pthis.RP_ToScreen_mat, textures: [textureMap[this.input_texture]] };
+                return { material: pthis.RP_ToScreen_mat, textures: [ textureMap[this.input_texture] ] }; // XXXX pthis or this ????
             },
             RC.RenderPass.SCREEN,
-            null,
+            null
         );
         this.RP_ToScreen.input_texture = "color_ssaa_down";
         this.RP_ToScreen.view_setup = function(vport) { this.viewport = vport; };
@@ -213,8 +306,12 @@ export class RendeQuTor
     make_RP_HighPassGaussBloom()
     {
         var pthis = this;
-
-        this.RP_HighPass_mat = new RC.CustomShaderMaterial("highPassReve");
+        // let hp = new RC.CustomShaderMaterial("highPass", {MODE: RC.HIGHPASS_MODE_BRIGHTNESS, targetColor: [0.2126, 0.7152, 0.0722], threshold: 0.75});
+        let hp = new RC.CustomShaderMaterial("highPass", { MODE: RC.HIGHPASS_MODE_DIFFERENCE,
+                                             targetColor: [0x0/255, 0x0/255, 0xff/255], threshold: 0.1});
+        console.log("XXXXXXXX", hp);
+        // let hp = new RC.CustomShaderMaterial("highPassReve");
+        this.RP_HighPass_mat = hp;
         this.RP_HighPass_mat.lights = false;
 
         this.RP_HighPass = new RC.RenderPass(
diff --git a/ui5/eve7/shaders/custom/copyDepth2RReve.frag b/ui5/eve7/shaders/custom/copyDepth2RReve.frag
new file mode 100644
index 00000000000..c95c4605edd
--- /dev/null
+++ b/ui5/eve7/shaders/custom/copyDepth2RReve.frag
@@ -0,0 +1,36 @@
+#version 300 es
+precision highp float;
+precision highp usampler2D;
+
+struct Material {
+    #if (TEXTURE)
+        // usampler2D texture0; // this fails in MeshRenderer, uniform setter for material
+        sampler2D texture0;
+    #fi
+};
+
+uniform Material material;
+
+#if (TEXTURE)
+    in vec2 fragUV;
+#fi
+
+out vec4 color;
+
+
+void main() {
+    #if (TEXTURE)
+        // color.r = 0.5370;
+        // color.r = fragUV.x;
+        // Logarithmic: (?)
+        // color.r = texture(material.texture0, fragUV).r;
+        // Linearize: (?)
+        color.r = pow(2.0, texture(material.texture0, fragUV).r) - 1.0;
+
+        // Or is this something with 1/z or w or what. Argh.
+        // Perhaphs the best way forward is to:
+        // 1. Implement picking on reduced target, say 16x16 or even 8x8
+        // 2. Attach float buffer to picking shader and write z that you want in there.
+        //    Or be a total pig and attach 3 buffers and store world xyz :)
+    #fi
+}
\ No newline at end of file
diff --git a/ui5/eve7/shaders/custom/copyDepth2RReve.vert b/ui5/eve7/shaders/custom/copyDepth2RReve.vert
new file mode 100644
index 00000000000..0ff3e68b0dc
--- /dev/null
+++ b/ui5/eve7/shaders/custom/copyDepth2RReve.vert
@@ -0,0 +1,20 @@
+#version 300 es
+precision highp float;
+
+in vec3 VPos; // Vertex position
+
+#if (TEXTURE)
+    in vec2 uv;  // Texture coordinate
+#fi
+
+// Output quad texture coordinates
+out vec2 fragUV;
+
+void main() {
+    gl_Position = vec4(VPos, 1.0);
+
+    #if (TEXTURE)
+        // Pass-through texture coordinate
+        fragUV = uv;
+    #fi
+}
\ No newline at end of file
diff --git a/ui5/eve7/shaders/programs.json b/ui5/eve7/shaders/programs.json
index b75e183336f..eef8be861b9 100644
--- a/ui5/eve7/shaders/programs.json
+++ b/ui5/eve7/shaders/programs.json
@@ -1,16 +1,24 @@
 {
     "custom_highPassReve": {
-        "description": "High Pass Filter fo REve.",
+        "description":  "High Pass Filter fo REve.",
         "shaders": {
-            "vertex": "custom/highPassReve.vert",
+            "vertex":   "custom/highPassReve.vert",
             "fragment": "custom/highPassReve.frag"
         }
     },
     "custom_lowPassReve": {
-        "description": "Low Pass Filter fo REve.",
+        "description":  "Low Pass Filter fo REve.",
         "shaders": {
-            "vertex": "custom/post_process/lowPassReve.vert",
+            "vertex":   "custom/post_process/lowPassReve.vert",
             "fragment": "custom/post_process/lowPassReve.frag"
         }
+    },
+
+    "custom_copyDepth2RReve" : {
+        "description":  "Copy depth buffer values to R component (R32F) for picking depth extraction.",
+        "shaders": {
+            "vertex":   "custom/copyDepth2RReve.vert",
+            "fragment": "custom/copyDepth2RReve.frag"
+        }
     }
-}
\ No newline at end of file
+}
-- 
GitLab