diff --git a/js/scripts/JSRoot3DPainter.js b/js/scripts/JSRoot3DPainter.js index 210ec7cdc6255..10a178a0f2936 100644 --- a/js/scripts/JSRoot3DPainter.js +++ b/js/scripts/JSRoot3DPainter.js @@ -503,6 +503,27 @@ } control.MainProcessMouseMove = function(evnt) { + // check timeout + var toutval = 100; + + // first time + var dt = new Date(); + if (!this.mouse_tmout) { + this.mouse_tmout = setTimeout(this.MainProcessMouseMove.bind(this,evnt), toutval); + this.tt = dt.getTime(); + return; + } + else { + var tdiff = dt.getTime() - this.tt; + if (tdiff < toutval) { + clearTimeout(this.mouse_tmout); + this.mouse_tmout = setTimeout(this.MainProcessMouseMove.bind(this,evnt), tdiff); + return; + } + } + this.tt = dt.getTime(); + //console.log("control.MainProcessMouseMove"); + if (this.control_active && evnt.buttons && (evnt.buttons & 2)) this.block_ctxt = true; // if right button in control was active, block next context menu diff --git a/js/scripts/JSRootGeoPainter.js b/js/scripts/JSRootGeoPainter.js index a21c7ded055bc..74a9f1b14f737 100644 --- a/js/scripts/JSRootGeoPainter.js +++ b/js/scripts/JSRootGeoPainter.js @@ -1915,6 +1915,24 @@ this._effectComposer = new THREE.EffectComposer( this._renderer ); this._effectComposer.addPass( new THREE.RenderPass( this._scene, this._camera ) ); + this._outlinePass = new THREE.OutlinePass( new THREE.Vector2( w, h ), this._scene, this._camera ); + this._outlinePass.edgeStrength = 7.5; + this._outlinePass.edgeGlow = 0.5; + this._outlinePass.edgeThickness = 1.0; + this._outlinePass.usePatternTexture = false; + this._outlinePass.downSampleRatio = 2; + + const sh = THREE.OutlinePass.selection_enum["select"]; // doesnt stand for spherical harmonics :P + THREE.OutlinePass.selection_atts[sh].visibleEdgeColor.set('#dd1111'); + THREE.OutlinePass.selection_atts[sh].hiddenEdgeColor.set('#1111dd'); + + this._effectComposer.addPass( this._outlinePass ); + + this._effectFXAA = new THREE.ShaderPass( THREE.FXAAShader ); + this._effectFXAA.uniforms[ 'resolution' ].value.set( 1 / w, 1 / h ); + this._effectFXAA.renderToScreen = true; + this._effectComposer.addPass( this._effectFXAA ); + if (this._enableSSAO) this.createSSAO(); @@ -3574,6 +3592,7 @@ this._camera.updateProjectionMatrix(); this._renderer.setSize( this._scene_width, this._scene_height, !this._fit_main_area ); this._effectComposer.setSize( this._scene_width, this._scene_height ); + this._effectFXAA.uniforms[ 'resolution' ].value.set( 1 / this._scene_width, 1 / this._scene_height ); if (!this.drawing_stage) this.Render3D(); } diff --git a/tutorials/eve7/lineset.C b/tutorials/eve7/lineset.C index 609d51a7b7e89..aa4616d61e9ff 100644 --- a/tutorials/eve7/lineset.C +++ b/tutorials/eve7/lineset.C @@ -1,5 +1,5 @@ /// \file -/// \ingroup tutorial_eve7 +/// \ingroup tutorial_eve /// Demonstrates usage of class REveStraightLineSet. /// /// \macro_code @@ -15,29 +15,43 @@ //#include namespace REX = ROOT::Experimental; -void lineset(Int_t nlines = 40, Int_t nmarkers = 4) -{ - auto eveMng = REX::REveManager::Create(); +REX::REveStraightLineSet* makeLineSet(Int_t nlines = 40, Int_t nmarkers = 4) +{ TRandom r(0); Float_t s = 100; auto ls = new REX::REveStraightLineSet(); - ls->SetMainColor(kBlue); - ls->SetMarkerColor(kRed); - for (Int_t i = 0; i < nlines; i++) { + for (Int_t i = 0; iAddLine( r.Uniform(-s,s), r.Uniform(-s,s), r.Uniform(-s,s), r.Uniform(-s,s), r.Uniform(-s,s), r.Uniform(-s,s)); // add random number of markers Int_t nm = Int_t(nmarkers* r.Rndm()); - for (Int_t m = 0; m < nm; m++) - ls->AddMarker(i, r.Rndm()); + for (Int_t m = 0; m < nm; m++) ls->AddMarker(i, r.Rndm()); } - ls->SetMarkerSize(1.5); - ls->SetMarkerStyle(4); - eveMng->GetEventScene()->AddElement(ls); + ls->SetMarkerSize(0.5); + ls->SetMarkerStyle(1); + REX::gEve->GetEventScene()->AddElement(ls); + + return ls; +} + +void lineset() +{ + auto eveMng = REX::REveManager::Create(); + + auto ls1 = makeLineSet(10, 50); + ls1->SetMainColor(kViolet); + ls1->SetName("LineSet_1"); + + auto ls2 = makeLineSet(3, 4); + ls2->SetMainColor(kBlue); + ls2->SetName("LineSet_2"); + //ls2->InitMainTrans(); + // ls2->RefMainTrans().Move3LF(40, 100, 100); + eveMng->Show(); } diff --git a/ui5/eve7/controller/GL.controller.js b/ui5/eve7/controller/GL.controller.js index 1d196c1c37fa8..1e6ae8a777665 100644 --- a/ui5/eve7/controller/GL.controller.js +++ b/ui5/eve7/controller/GL.controller.js @@ -295,7 +295,8 @@ sap.ui.define([ var options = ""; - if (this.kind != "3D") options = "ortho_camera"; + // options += " black, "; + if (this.kind != "3D") options += "ortho_camera"; // TODO: should be specified somehow in XML file @@ -321,28 +322,55 @@ sap.ui.define([ this.geo_painter._camera.bottom = -this.getView().$().height(); this.geo_painter._camera.updateProjectionMatrix(); } + painter._controls.ProcessMouseMove = function(intersects) { + var active_mesh = null, tooltip = null, resolve = null, names = [], geo_object, geo_index; + + // try to find mesh from intersections + for (var k=0;k")==0) + info = painter.GetItemName() + info.substr(6); + + names.push(info); + + if (!active_mesh) { + active_mesh = obj; + tooltip = info; + geo_object = obj.geo_object; + if (obj.get_ctrl) { + geo_index = obj.get_ctrl().extractIndex(intersects[k]); + if ((geo_index !== undefined) && (typeof tooltip == "string")) tooltip += " indx:" + JSON.stringify(geo_index); + } + if (active_mesh.stack) resolve = painter.ResolveStack(active_mesh.stack); + } + } + + //painter.HighlightMesh(active_mesh, undefined, geo_object, geo_index); AMT override + if (active_mesh && active_mesh.get_ctrl()) active_mesh.get_ctrl().setHighlight( 0xffaa33, geo_index); + + if (painter.options.update_browser) { + if (painter.options.highlight && tooltip) names = [ tooltip ]; + painter.ActivateInBrowser(names); + } + + if (!resolve || !resolve.obj) return tooltip; + + var lines = JSROOT.GEO.provideInfo(resolve.obj); + lines.unshift(tooltip); + + return { name: resolve.obj.fName, title: resolve.obj.fTitle || resolve.obj._typename, lines: lines }; + } // this.geo_painter._highlight_handlers = [ this ]; // register ourself for highlight handling this.last_highlight = null; - this.composer = this.geo_painter._effectComposer; - let width = this.geo_painter._scene_width; - let height = this.geo_painter._scene_height; - - this.outlinePass = new THREE.OutlinePass( new THREE.Vector2( width, height ), this.geo_painter._scene, this.geo_painter._camera ); - this.outlinePass.edgeStrength = 7.5; - this.outlinePass.edgeGlow = 0.5; - this.outlinePass.edgeThickness = 1.0; - this.outlinePass.usePatternTexture = false; - this.outlinePass.downSampleRatio = 2; - this.outlinePass.visibleEdgeColor.set('#dd1111'); - this.outlinePass.hiddenEdgeColor.set('#1111dd'); - this.composer.addPass( this.outlinePass ); - - this.effectFXAA = new THREE.ShaderPass( THREE.FXAAShader ); - this.effectFXAA.uniforms[ 'resolution' ].value.set( 1 / width, 1 / height ); - this.effectFXAA.renderToScreen = true; - this.composer.addPass( this.effectFXAA ); + // outlinePass passthrough + this.outlinePass = this.geo_painter._outlinePass; // create only when geo painter is ready this.createScenes(); @@ -364,7 +392,6 @@ sap.ui.define([ this.getView().$().css("overflow", "hidden").css("width", "100%").css("height", "100%"); if (this.geo_painter){ this.geo_painter.CheckResize(); - this.effectFXAA.uniforms[ 'resolution' ].value.set( 1 / this.geo_painter._scene_width, 1 / this.geo_painter._scene_height ); } } diff --git a/ui5/eve7/lib/EveElements.js b/ui5/eve7/lib/EveElements.js index 4dfc930974177..49fa68457f0ce 100644 --- a/ui5/eve7/lib/EveElements.js +++ b/ui5/eve7/lib/EveElements.js @@ -573,7 +573,7 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function(EveManager) { var color = JSROOT.Painter.root_colors[m.eve_el.fMainColor]; var lineMaterial = new THREE.LineBasicMaterial({ color: color, linewidth: 4 }); var line = new THREE.LineSegments(geom, lineMaterial); - dest.add(line); + dest.push(line); var el = m.eve_el, mindx = [] @@ -592,7 +592,8 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function(EveManager) { if (mindx.length > 0) { - var pnts = new JSROOT.Painter.PointsCreator(mindx.length, true, 5); + let mark_size = 5; + var pnts = new JSROOT.Painter.PointsCreator(mindx.length, true, mark_size); var arr = m.children[1].geometry.getAttribute("position").array; @@ -602,7 +603,8 @@ sap.ui.define(['rootui5/eve7/lib/EveManager'], function(EveManager) { pnts.AddPoint(arr[p], arr[p+1], arr[p+2] ); } var mark = pnts.CreatePoints(color); - dest.add(mark); + mark.material.size = mark_size; + dest.push(mark); } } diff --git a/ui5/eve7/lib/EveManager.js b/ui5/eve7/lib/EveManager.js index f39a8fd4036a2..8633252733e43 100644 --- a/ui5/eve7/lib/EveManager.js +++ b/ui5/eve7/lib/EveManager.js @@ -301,11 +301,11 @@ sap.ui.define([], function() { var mother = this.GetElement(motherId); var mc = mother.childs; for (var i = 0; i < mc.length; ++i) { - + if (mc[i].fElementId === elId) { mc.splice(i, 1); } - } + } delete this.map[elId]; delSet.delete(elId); @@ -315,7 +315,7 @@ sap.ui.define([], function() { } //______________________________________________________________________________ - + EveManager.prototype.ImportSceneChangeJson = function(msg) { var arr = msg.arr; @@ -329,12 +329,12 @@ sap.ui.define([], function() { // notify for element removal var removedIds = msg.header["removedElements"]; - // do we need this? -- AMT this is intended to freeze redraws + // do we need this? -- AMT this is intended to freeze redraws this.callSceneReceivers(scene, "beginChanges"); // notify controllers this.callSceneReceivers(scene, "elementsRemoved", removedIds); - + var delSet = new Set(); for (var r = 0; r < removedIds.length; ++r) { var id = removedIds[r]; @@ -347,8 +347,8 @@ sap.ui.define([], function() { console.log("going to call RecursiveRemove .... ", this.map[id]); this.RecursiveRemove(this.GetElement(id), delSet); console.log("complete RecursiveREmove ", delSet); - } - + } + // wait for binary if needed if (msg.header.fTotalBinarySize) { @@ -440,7 +440,7 @@ sap.ui.define([], function() { var treeRebuild = header.removedElements.length || (arr.length != nModified ); if (treeRebuild) this.InvokeReceivers("update", null, 0, this); - + this.callSceneReceivers(scene, "endChanges", treeRebuild); }, @@ -581,64 +581,93 @@ sap.ui.define([], function() { { if (el.fName == "Global Selection") this.global_selection_id = el.fElementId; if (el.fName == "Global Highlight") this.global_highlight_id = el.fElementId; - - // el._selection_set = new Set; - el._is_registered = true; el.prev_sel_list = []; } - console.log("And now process the bloody selection.", el.prev_sel_list, el.sel_list) - - // Outline of optimized selection update (to avoid recreating selected - // representations). - /* - all_new_selected = generate_full_selection_set(el['sel_list']); + console.log("==============================And now process the bloody selection.", el.prev_sel_list, el.sel_list); + + var oldMap = new Map(); + el.prev_sel_list.forEach(function(rec) { + var iset = new Set(rec.sec_idcs); + var x = {"valid" : true, "implied" : rec.implied, "set":iset }; + oldMap.set(rec.primary, x); + }); + console.log("-- oldMap", oldMap); + + var newMap = new Map ; + el.sel_list.forEach(function(rec) { + console.log("add new primary ", rec.primary); + var iset = new Set(rec.sec_idcs); + var x = {"valid" : true, "implied" : rec.implied, "set":iset }; + newMap.set(rec.primary, x); + }); + console.log("-- newMap --", newMap); + + + // remove identicals from old and new map + for ( var id in oldMap ) { + if (id in newMap) { + var oldSet = oldMap[id].set; + var newSet = newMap[id].set; + + var nm = 0; + for (var elem of oldSet) { + if (newSet.delete(elem)) { + nm++; + } + } - for (xx in el['prev_all_selected'] and not in all_new_selected) - { - deselect(xx); - } - for (xx in all_new_selected) - { - if (xx not in el['prev_all_selected']) - { - select(xx); - } - else - { - // implied selected set or secondary indices can change. - check_if_select_records_are_the_same_and_update_if_needed(); + // invalidate if sets are empty or identical + if (nm == oldSet.length && newSet.length == 0) { + oldMap[id].valid = false; + newMap[id].valid = false; + // console.log("EveManager.prototype.UT_Selection_Refresh_State identical sets for primary", id); + } } } - el['prev_all_selected'] = all_new_selected; - */ - - // Dummy update --- unselect all, select all. - for (var srec of el.prev_sel_list) - { - this.UnselectElement(el, srec.primary); - - for (var sel of srec.implied) + var pthis = this; + var changedSet = new Set(); + for (var [id, value] of oldMap.entries()) { + console.log("unselect ", el.fName, " id == ", id); + this.UnselectElement(el, id); + var iel = pthis.GetElement(id); + changedSet.add(iel.fSceneId); + for (var imp of value.implied) { - this.UnselectElement(el, sel); + this.UnselectElement(el, imp); + changedSet.add(pthis.GetElement(imp).fSceneId); } } - for (var srec of el.sel_list) - { - this.SelectElement(el, srec.primary, srec.sec_idcs); + for (var [id, value] of newMap.entries()) { + var secIdcs = Array.from(value.set); + var iel = pthis.GetElement(id); + if (!iel) { + // console.log("EveManager.prototype.UT_Selection_Refresh_State this should not happen ", iel); + continue; + } + changedSet.add(iel.fSceneId); + this.SelectElement(el, id, secIdcs); - for (sel of srec.implied) + for (var imp of value.implied) { - this.SelectElement(el, sel, srec.sec_idcs); + this.SelectElement(el, imp, secIdcs); + changedSet.add(pthis.GetElement(imp).fSceneId); } } el.prev_sel_list = el.sel_list; el.sel_list = []; + // redraw + for (let item of changedSet) { + console.log(item); + var scene = this.GetElement(item); + this.callSceneReceivers(scene, "endChanges"); + } + // XXXX Oh, blimy, on first arrival, if selection is set, the selected // elements have not yet been received and so this will fail. Also true // for newly subscribed scenes, once we start supporting this. diff --git a/ui5/eve7/lib/EveScene.js b/ui5/eve7/lib/EveScene.js index 04feff67dc84a..3c0006196ca1d 100644 --- a/ui5/eve7/lib/EveScene.js +++ b/ui5/eve7/lib/EveScene.js @@ -252,7 +252,6 @@ sap.ui.define([ EveScene.prototype.processElementSelected = function(obj3d, col, indx, evnt) { // MT BEGIN - console.log("EveScene.prototype.processElementSelected", obj3d, col, indx, evnt); var is_multi = evnt && evnt.ctrlKey; @@ -344,17 +343,47 @@ sap.ui.define([ /** interactive handler */ EveScene.prototype.processElementHighlighted = function(obj3d, col, indx, evnt) { - var id = obj3d.mstrId; - // id = obj3d.eveId; + // Need check for duplicates before call server, else server will un-higlight highlighted element + // console.log("EveScene.prototype.processElementHighlighted", obj3d, col, indx, evnt); + var is_multi = false; + var is_secsel = indx !== undefined; - // MT XXXX - // console.log("EveScene.prototype.processElementHighlighted", obj3d, col, indx, evnt); + var so = this.mgr.GetElement(this.mgr.global_highlight_id); + var a = so.prev_sel_list; + + // AMT presume there is no multiple highlight and multiple secondary selections + // if that is the case in the futre write data in set and comapre sets + + // console.log("EveScene.prototype.processElementHighlighted compare Reveselection ", a[0], "incoming ", obj3d.eveId,indx); + if (a.length == 1 ) { + var h = a[0]; + if (h.primary == obj3d.eveId || h.primary == obj3d.mstrId ) { + if (indx) { + if (h.sec_idcs && h.sec_idcs[0] == indx) { + // console.log("EveScene.prototype.processElementHighlighted processElementHighlighted same index "); + return true; + } + } + if (!indx && !h.sec_idcs.length) { + // console.log("processElementHighlighted primARY SElection not changed "); + return true; + } + } + } + + var fcall = "NewElementPicked(" + obj3d.eveId + `, ${is_multi}, ${is_secsel}`; + if (is_secsel) + { + fcall += ", { " + (Array.isArray(indx) ? indx.join(", ") : indx) + " }"; + } + fcall += ")"; - this.setElementHighlighted(id, col, indx, true); - this.mgr.invokeInOtherScenes(this, "setElementHighlighted", id, col, indx); + this.mgr.SendMIR({ "mir": fcall, + "fElementId": this.mgr.global_highlight_id, + "class": "REX::REveSelection" + }); - // when true returns, controller will not try to render itself return true; } @@ -459,10 +488,18 @@ sap.ui.define([ EveScene.prototype.SelectElement = function(selection_obj, element_id, sec_idcs) { + var stype = selection_obj.fName.endsWith("Selection") ? "select" : "highlight"; + var estype = THREE.OutlinePass.selection_enum[stype]; + + let res = { + "sel_type": estype, + "sec_sel": false, + "geom": [] + }; var obj3d = this.getObj3D( element_id ); if ( ! (selection_obj.fElementId in this.viewer.outlinePass.id2obj_map)) - this.viewer.outlinePass.id2obj_map[selection_obj.fElementId] = []; + this.viewer.outlinePass.id2obj_map[selection_obj.fElementId] = []; var dest = this.viewer.outlinePass.id2obj_map[selection_obj.fElementId]; @@ -470,21 +507,15 @@ sap.ui.define([ if(sec_idcs === undefined || sec_idcs.length == 0) { - dest[element_id] = obj3d; + res.geom.push(obj3d); } else { var ctrl = obj3d.get_ctrl(); - // AMT todo check for memory leaks - var x = new THREE.Object3D(); - ctrl.DrawForSelection(sec_idcs, x); - console.log("draw for selection exit ", x); - // AMT for debugging purposes take only first child - var fe = x.children[0]; - dest[element_id] = fe; + ctrl.DrawForSelection(sec_idcs, res.geom); + res.sec_sel = true; } - - this.viewer.render(); + dest[element_id] = res; } EveScene.prototype.UnselectElement = function(selection_obj, element_id) @@ -492,8 +523,6 @@ sap.ui.define([ if (selection_obj.fElementId in this.viewer.outlinePass.id2obj_map) { delete this.viewer.outlinePass.id2obj_map[selection_obj.fElementId][element_id]; - - this.viewer.render(); } } diff --git a/ui5/eve7/lib/OutlinePass.js b/ui5/eve7/lib/OutlinePass.js index 86f6864192146..74f7c1ee06f47 100644 --- a/ui5/eve7/lib/OutlinePass.js +++ b/ui5/eve7/lib/OutlinePass.js @@ -7,16 +7,19 @@ THREE.OutlinePass = function ( resolution, scene, camera ) { // [{ "index": number, "isPoints": boolean, "pointSize": number, "vertShader": string, "fragShader":string },......] this.renderScene = scene; this.renderCamera = camera; - this.selectedObjects = []; + + // [fElementId][elementId] -> { "sel_type": THREE.OutlinePass.selection_enum, "sec_sel": boolean, "geom": Primitive<> } this.id2obj_map = {}; - this.visibleEdgeColor = new THREE.Color( 1, 1, 1 ); - this.hiddenEdgeColor = new THREE.Color( 0.1, 0.04, 0.02 ); + // R: Primitives + this._selectedObjects = []; + // [C1]: Selection Types - [C2]: Attributes(color, size, etc...) - [R2]: Primitives + this._groups = Array.from(Array(THREE.OutlinePass.selection_enum.total), () => []); // ES6 (could be replaced with vanilla Js) + this.edgeGlow = 0.0; this.usePatternTexture = false; this.edgeThickness = 1.0; this.edgeStrength = 3.0; this.downSampleRatio = 2; - this.pulsePeriod = 0; THREE.Pass.call( this ); @@ -29,9 +32,20 @@ THREE.OutlinePass = function ( resolution, scene, camera ) { this.maskBufferMaterial = new THREE.MeshBasicMaterial( { color: 0xffffff } ); this.maskBufferMaterial.side = THREE.DoubleSide; - this.renderTargetMaskBuffer = new THREE.WebGLRenderTarget( this.resolution.x, this.resolution.y, pars ); - this.renderTargetMaskBuffer.texture.name = "OutlinePass.mask"; - this.renderTargetMaskBuffer.texture.generateMipmaps = false; + + this.renderTargetMaskBuffer = []; + // +1 extra for the "accumulated" result + for(let i = 0; i < THREE.OutlinePass.selection_enum.total; ++i){ + let maskBuffer = new THREE.WebGLRenderTarget( this.resolution.x, this.resolution.y, pars ) + maskBuffer.texture.name = "OutlinePass.submask"+i; + maskBuffer.texture.generateMipmaps = false; + + this.renderTargetMaskBuffer.push(maskBuffer); + } + + this.renderTargetMaskBufferMain = new THREE.WebGLRenderTarget( this.resolution.x, this.resolution.y, pars ); + this.renderTargetMaskBufferMain.texture.name = "OutlinePass.mask"; + this.renderTargetMaskBufferMain.texture.generateMipmaps = false; this.depthMaterial = new THREE.MeshDepthMaterial(); this.depthMaterial.side = THREE.DoubleSide; @@ -65,8 +79,8 @@ THREE.OutlinePass = function ( resolution, scene, camera ) { this.renderTargetEdgeBuffer2.texture.name = "OutlinePass.edge2"; this.renderTargetEdgeBuffer2.texture.generateMipmaps = false; - var MAX_EDGE_THICKNESS = 4; - var MAX_EDGE_GLOW = 4; + const MAX_EDGE_THICKNESS = 4; + const MAX_EDGE_GLOW = 4; this.separableBlurMaterial1 = this.getSeperableBlurMaterial( MAX_EDGE_THICKNESS ); this.separableBlurMaterial1.uniforms[ "texSize" ].value = new THREE.Vector2( resx, resy ); @@ -128,6 +142,39 @@ THREE.OutlinePass.prototype = Object.assign( Object.create( THREE.Pass.prototype constructor: THREE.OutlinePass, + parseAtts: function(object, groups){ + let found = false; + // loop over groups + for (let z = 0; z < groups.length; ++z){ + // loop over all the elements of a group + //for (let w = 0; w < groups[z].length; ++w) + + const w = 0; // check only the first element of the group! + { + let pass = (object.type === "Points") ? (groups[z][w].material.size === object.material.size) : true; + + if( pass ){ + // final check + if( groups[z][w]["vertShader"] === object["vertShader"] && + groups[z][w]["fragShader"] === object["fragShader"] && + groups[z][w].visibleEdgeColor === object.visibleEdgeColor && + groups[z][w].hiddenEdgeColor === object.hiddenEdgeColor + ) { + groups[z].push(object); + found = true; + break; + } + } + } + if(found) + break; + } + + if(!found){ + groups.push([object]); + } + }, + checkForCustomAtts: function(){ /* this.atts = { @@ -149,60 +196,75 @@ THREE.OutlinePass.prototype = Object.assign( Object.create( THREE.Pass.prototype } } */ + + // one array for each selection type + this._groups = Array.from(Array(THREE.OutlinePass.selection_enum.total), () => []); - let groups = []; - for (let i = 0; i < this.selectedObjects.length; ++i){ - const object = this.selectedObjects[i]; - - // treat Mesh and LineSegments as the same - if(object.type === "Mesh" || object.type === "LineSegments" ) - { - groups[0] = groups[0] || []; - groups[0].push(object); - } - else if(object.type === "Points") - { - let found = false; - // loop over groups - for (let z = 1; z < groups.length; ++z){ - // loop over all the elements of a group - for (let w = 0; w < z.length; ++w){ - // if the objects have the same attributes - if( - this.selectedObjects[z][w].type === object.type && - this.selectedObjects[z][w].material.size === object.material.size && - this.selectedObjects[z][w]["vertShader"] === object["vertShader"] && - this.selectedObjects[z][w]["fragShader"] === object["fragShader"] - ){ - groups[z] = groups[z] || []; - groups[z].push(object); - found = true; - break; - } - } - if(found) - break; - } - - if(!found){ - groups[i+1] = groups[i+1] || []; - groups[i+1].push(object); - } - } - else - { - console.error("unknown type of geometry! fallback to 0"); - groups[0] = groups[0] || []; - groups[0].push(object); + // fill in "this._groups" + for (const obj of this._selectedObjects){ + for(const geom of obj.geom){ + this.parseAtts(geom, this._groups[obj.sel_type]); } } - this.groups = groups/*.filter(Array)*/; - // console.log(groups); + + // for (let i = 0; i < this.selectedObjects.length; ++i){ + // const object = this.selectedObjects[i]; + + // // treat Mesh and LineSegments as the same + // if(object.type === "Mesh" || object.type === "LineSegments" ) + // { + // groups[0] = groups[0] || []; + // groups[0].push(object); + // } + // else if(object.type === "Points") + // { + // let found = false; + // // loop over groups + // for (let z = 1; z < groups.length; ++z){ + // // loop over all the elements of a group + // for (let w = 0; w < z.length; ++w){ + // // if the objects have the same attributes + // if( + // this.selectedObjects[z][w].type === object.type && + // this.selectedObjects[z][w].material.size === object.material.size && + // this.selectedObjects[z][w]["vertShader"] === object["vertShader"] && + // this.selectedObjects[z][w]["fragShader"] === object["fragShader"] + // ){ + // groups[z].push(object); + // found = true; + // break; + // } + // } + // if(found) + // break; + // } + + // if(!found){ + // groups.push([object]); + // } + // } + // else if(object.type === "Group") + // { + // for (const child of object.children){ + + // } + // } + // else + // { + // console.error("unknown type of geometry! fallback to 0"); + // groups[0] = groups[0] || []; + // groups[0].push(object); + // } + // } + // this._groups = groups.filter(Array); }, dispose: function () { - this.renderTargetMaskBuffer.dispose(); + this.renderTargetMaskBufferMain.dispose(); + for(const fbo of this.renderTargetMaskBuffer) + fbo.dispose(); + this.renderTargetDepthBuffer.dispose(); this.renderTargetMaskDownSampleBuffer.dispose(); this.renderTargetBlurBuffer1.dispose(); @@ -214,7 +276,9 @@ THREE.OutlinePass.prototype = Object.assign( Object.create( THREE.Pass.prototype setSize: function ( width, height ) { - this.renderTargetMaskBuffer.setSize( width, height ); + this.renderTargetMaskBufferMain.setSize( width, height ); + for(const fbo of this.renderTargetMaskBuffer) + fbo.setSize( width, height ); var resx = Math.round( width / this.downSampleRatio ); var resy = Math.round( height / this.downSampleRatio ); @@ -255,8 +319,13 @@ THREE.OutlinePass.prototype = Object.assign( Object.create( THREE.Pass.prototype } - for(const obj of ((Array.isArray(object) ? object : (object && [object])) || this.selectedObjects)){ - obj.traverse( gatherSelectedMeshesCallBack ); + for(const obj of (object || this._selectedObjects)){ + if(obj.geom){ + for(const geom of obj.geom) + geom.traverse( gatherSelectedMeshesCallBack ); + } else { + obj.traverse( gatherSelectedMeshesCallBack ); + } } }, @@ -271,11 +340,9 @@ THREE.OutlinePass.prototype = Object.assign( Object.create( THREE.Pass.prototype } - for ( var i = 0; i < this.selectedObjects.length; i ++ ) { - - var selectedObject = this.selectedObjects[ i ]; - selectedObject.traverse( gatherSelectedMeshesCallBack ); - + for(const obj of this._selectedObjects){ + for(const geom of obj.geom) + geom.traverse( gatherSelectedMeshesCallBack ); } function VisibilityChangeCallBack( object ) { @@ -326,7 +393,7 @@ THREE.OutlinePass.prototype = Object.assign( Object.create( THREE.Pass.prototype }, - draw: function(renderer, writeBuffer, readBuffer, maskActive, group, index){ + draw: function(renderer, group, selection_type){ this.changeVisibilityOfSelectedObjects(true, group); if(group[0].type === "Points"){ @@ -334,17 +401,33 @@ THREE.OutlinePass.prototype = Object.assign( Object.create( THREE.Pass.prototype if(group[0]["vertShader"]) this.prepareMaskMaterial.vertexShader = group[0]["vertShader"]; if(group[0]["fragShader"]) this.prepareMaskMaterial.fragmentShader = group[0]["fragShader"]; } + renderer.setRenderTarget( this.renderTargetMaskBufferMain ); + renderer.render( this.renderScene, this.renderCamera ); + + renderer.setRenderTarget( this.renderTargetMaskBuffer[selection_type] ); + renderer.clear(); renderer.render( this.renderScene, this.renderCamera ); this.changeVisibilityOfSelectedObjects(false, group); }, render: function ( renderer, writeBuffer, readBuffer, deltaTime, maskActive ) { - this.selectedObjects = Object.values(this.id2obj_map).flat(); - console.log(this.selectedObjects); + this._selectedObjects = Object.values(this.id2obj_map).flat(); + // console.log(this.selectedObjects); // debugger; - if ( this.selectedObjects.length > 0 ) { + if ( this._selectedObjects.length > 0 ) { + // fetch objects that were created after secondary selection + let sec_sel = this._selectedObjects.map(function(v){ + if(v.sec_sel) return v.geom; + }); + sec_sel = sec_sel.flat(); + // debugger; + + // add sec_sel elements in renderScene + for(const obj of sec_sel) + if(obj) this.renderScene.add(obj); + this.oldClearColor.copy( renderer.getClearColor() ); this.oldClearAlpha = renderer.getClearAlpha(); var oldAutoClear = renderer.autoClear; @@ -378,14 +461,16 @@ THREE.OutlinePass.prototype = Object.assign( Object.create( THREE.Pass.prototype this.prepareMaskMaterial.uniforms[ "depthTexture" ].value = this.renderTargetDepthBuffer.texture; this.prepareMaskMaterial.uniforms[ "textureMatrix" ].value = this.textureMatrix; - renderer.setRenderTarget( this.renderTargetMaskBuffer ); + renderer.setRenderTarget( this.renderTargetMaskBufferMain ); renderer.clear(); this.checkForCustomAtts(); + //console.log(this._groups); - for(const group of this.groups){ - if(group.length > 0) - this.draw(renderer, writeBuffer, readBuffer, maskActive, group); + for(let i = 0; i < THREE.OutlinePass.selection_enum.total; ++i){ + for(const group of this._groups[i]) + if(group.length > 0) + this.draw(renderer, group, i); } // if(this.atts.total > 0){ @@ -407,41 +492,68 @@ THREE.OutlinePass.prototype = Object.assign( Object.create( THREE.Pass.prototype // this.draw(renderer, writeBuffer, readBuffer, maskActive, this.selectedObjects, -1); // } - // enable back all selected elements + this.renderScene.overrideMaterial = null; + + // enable all elements this.changeVisibilityOfSelectedObjects(true); + this.changeVisibilityOfNonSelectedObjects(true); - this.renderScene.overrideMaterial = null; - this.changeVisibilityOfNonSelectedObjects( true ); - this.renderScene.background = currentBackground; // 2. Downsample to Half resolution - this.quad.material = this.materialCopy; - this.copyUniforms[ "tDiffuse" ].value = this.renderTargetMaskBuffer.texture; - renderer.setRenderTarget( this.renderTargetMaskDownSampleBuffer ); + + // clear stuff + renderer.setRenderTarget( this.renderTargetEdgeBuffer1 ); renderer.clear(); - renderer.render( this.scene, this.camera ); - - this.tempPulseColor1.copy( this.visibleEdgeColor ); - this.tempPulseColor2.copy( this.hiddenEdgeColor ); - - if ( this.pulsePeriod > 0 ) { + + for(let i = 0; i < THREE.OutlinePass.selection_enum.total; ++i){ + let group = this._groups[i]; + if(group.length > 0){ + this.quad.material = this.materialCopy; + this.copyUniforms[ "tDiffuse" ].value = this.renderTargetMaskBuffer[i].texture; + renderer.setRenderTarget( this.renderTargetMaskDownSampleBuffer ); + renderer.clear(); + renderer.render( this.scene, this.camera ); - var scalar = ( 1 + 0.25 ) / 2 + Math.cos( performance.now() * 0.01 / this.pulsePeriod ) * ( 1.0 - 0.25 ) / 2; - this.tempPulseColor1.multiplyScalar( scalar ); - this.tempPulseColor2.multiplyScalar( scalar ); + // this.changeVisibilityOfSelectedObjects(false); + + // this.changeVisibilityOfSelectedObjects(true, group); + + // 3. Apply Edge Detection Pass + this.quad.material = this.edgeDetectionMaterial; + this.edgeDetectionMaterial.uniforms[ "maskTexture" ].value = this.renderTargetMaskDownSampleBuffer.texture; + this.edgeDetectionMaterial.uniforms[ "texSize" ].value = new THREE.Vector2( this.renderTargetMaskDownSampleBuffer.width, this.renderTargetMaskDownSampleBuffer.height ); + renderer.setRenderTarget( this.renderTargetEdgeBuffer1 ); + const att = THREE.OutlinePass.selection_atts[i]; + this.edgeDetectionMaterial.uniforms[ "visibleEdgeColor" ].value = att.visibleEdgeColor; + this.edgeDetectionMaterial.uniforms[ "hiddenEdgeColor" ].value = att.hiddenEdgeColor; + renderer.render( this.scene, this.camera ); + + // this.changeVisibilityOfSelectedObjects(false, group); + } + + // this.changeVisibilityOfSelectedObjects(true); } - - // 3. Apply Edge Detection Pass - this.quad.material = this.edgeDetectionMaterial; - this.edgeDetectionMaterial.uniforms[ "maskTexture" ].value = this.renderTargetMaskDownSampleBuffer.texture; - this.edgeDetectionMaterial.uniforms[ "texSize" ].value = new THREE.Vector2( this.renderTargetMaskDownSampleBuffer.width, this.renderTargetMaskDownSampleBuffer.height ); - this.edgeDetectionMaterial.uniforms[ "visibleEdgeColor" ].value = this.tempPulseColor1; - this.edgeDetectionMaterial.uniforms[ "hiddenEdgeColor" ].value = this.tempPulseColor2; - renderer.setRenderTarget( this.renderTargetEdgeBuffer1 ); - renderer.clear(); - renderer.render( this.scene, this.camera ); + + + + //>----------------- + // for(let i = 0; i < THREE.OutlinePass.selection_enum.total; ++i){ + // const sel = this.sel[i]; + // if(sel.length > 0){ + // this.changeVisibilityOfSelectedObjects(true, sel); + + // // 3. Apply Edge Detection Pass + // const att = THREE.OutlinePass.selection_atts[i]; + // this.edgeDetectionMaterial.uniforms[ "visibleEdgeColor" ].value = att.visibleEdgeColor; + // this.edgeDetectionMaterial.uniforms[ "hiddenEdgeColor" ].value = att.hiddenEdgeColor; + // renderer.render( this.scene, this.camera ); + + // this.changeVisibilityOfSelectedObjects(false, sel); + // } + // } + // this.changeVisibilityOfSelectedObjects(true); // 4. Apply Blur on Half res this.quad.material = this.separableBlurMaterial1; @@ -472,7 +584,7 @@ THREE.OutlinePass.prototype = Object.assign( Object.create( THREE.Pass.prototype // Blend it additively over the input texture this.quad.material = this.overlayMaterial; - this.overlayMaterial.uniforms[ "maskTexture" ].value = this.renderTargetMaskBuffer.texture; + this.overlayMaterial.uniforms[ "maskTexture" ].value = this.renderTargetMaskBufferMain.texture; this.overlayMaterial.uniforms[ "edgeTexture1" ].value = this.renderTargetEdgeBuffer1.texture; this.overlayMaterial.uniforms[ "edgeTexture2" ].value = this.renderTargetEdgeBuffer2.texture; this.overlayMaterial.uniforms[ "patternTexture" ].value = this.patternTexture; @@ -489,6 +601,10 @@ THREE.OutlinePass.prototype = Object.assign( Object.create( THREE.Pass.prototype renderer.setClearColor( this.oldClearColor, this.oldClearAlpha ); renderer.autoClear = oldAutoClear; + + // remove sec_sel elements from renderScene + for(const obj of sec_sel) + if(obj) this.renderScene.remove(obj); } else { this.quad.material = this.materialCopy; this.copyUniforms[ "tDiffuse" ].value = readBuffer.texture; @@ -718,3 +834,30 @@ if(false){ THREE.OutlinePass.BlurDirectionX = new THREE.Vector2( 1.0, 0.0 ); THREE.OutlinePass.BlurDirectionY = new THREE.Vector2( 0.0, 1.0 ); + +THREE.OutlinePass.selection_enum = { + "select": 0, + "highlight": 1, + "total": 2 +}; + +THREE.OutlinePass.selection_atts = [ + { + visibleEdgeColor: new THREE.Color( 1, 0, 0 ), + hiddenEdgeColor: new THREE.Color( 1, 0, 0.5 ) + // hiddenEdgeColoe: new THREE.Color( 0.1, 0.04, 0.02 ) + // edgeGlow: 0.0, + // usePatternTexture: false, + // edgeThickness: 1.0, + // edgeStrength: 3.0, + }, + { + visibleEdgeColor: new THREE.Color( 0, 1, 0 ), + hiddenEdgeColor: new THREE.Color( 0, 1, 0.5 ) + // hiddenEdgeColor: new THREE.Color( 0.1, 0.04, 0.02 ) + // edgeGlow: 0.0, + // usePatternTexture: false, + // edgeThickness: 1.0, + // edgeStrength: 3.0, + } +];