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