diff --git a/Sources/Rendering/Core/Glyph3DMapper/test/testGlyph3DMapperShiftScale.js b/Sources/Rendering/Core/Glyph3DMapper/test/testGlyph3DMapperShiftScale.js new file mode 100644 index 00000000000..ac33024eed5 --- /dev/null +++ b/Sources/Rendering/Core/Glyph3DMapper/test/testGlyph3DMapperShiftScale.js @@ -0,0 +1,98 @@ +import test from 'tape'; +import testUtils from 'vtk.js/Sources/Testing/testUtils'; + +import 'vtk.js/Sources/Rendering/Misc/RenderingAPIs'; +import vtkActor from 'vtk.js/Sources/Rendering/Core/Actor'; +import vtkCellArray from 'vtk.js/Sources/Common/Core/CellArray'; +import vtkGlyph3DMapper from 'vtk.js/Sources/Rendering/Core/Glyph3DMapper'; +import vtkPolyData from 'vtk.js/Sources/Common/DataModel/PolyData'; +import vtkRenderWindow from 'vtk.js/Sources/Rendering/Core/RenderWindow'; +import vtkRenderer from 'vtk.js/Sources/Rendering/Core/Renderer'; + +function makePolyDataFromPoints(coords) { + const polyData = vtkPolyData.newInstance(); + polyData.getPoints().setData(Float32Array.from(coords), 3); + return polyData; +} + +function updatePoints(polyData, coords) { + polyData.getPoints().setData(Float32Array.from(coords), 3); + polyData.getPoints().modified(); + polyData.modified(); +} + +function makeLineGlyphSource() { + const source = makePolyDataFromPoints([-0.5, 0, 0, 0.5, 0, 0]); + source.setLines( + vtkCellArray.newInstance({ + values: new Uint32Array([2, 0, 1]), + }) + ); + return source; +} + +function getActivePrimitiveCABOs(openGLRenderWindow, mapper) { + const openGLMapper = openGLRenderWindow.getViewNodeFor(mapper); + const primitives = openGLMapper.getReferenceByName('primitives'); + return primitives + .map((primitive) => primitive.getCABO()) + .filter((cabo) => cabo.getElementCount() > 0); +} + +test.onlyIfWebGL( + 'Test vtkGlyph3DMapper clears source VBO shift/scale after shifted glyph centers', + (t) => { + const gc = testUtils.createGarbageCollector(t); + + const container = document.querySelector('body'); + const renderWindowContainer = gc.registerDOMElement( + document.createElement('div') + ); + container.appendChild(renderWindowContainer); + + const renderWindow = gc.registerResource(vtkRenderWindow.newInstance()); + const renderer = gc.registerResource(vtkRenderer.newInstance()); + renderWindow.addRenderer(renderer); + + const centers = gc.registerResource( + makePolyDataFromPoints([10000000, 0, 0, 10000001, 0, 0]) + ); + const source = gc.registerResource(makeLineGlyphSource()); + const mapper = gc.registerResource(vtkGlyph3DMapper.newInstance()); + const actor = gc.registerResource(vtkActor.newInstance()); + + mapper.setInputData(centers, 0); + mapper.setInputData(source, 1); + mapper.setScalarVisibility(false); + + actor.setMapper(mapper); + renderer.addActor(actor); + + const openGLRenderWindow = gc.registerResource( + renderWindow.newAPISpecificView() + ); + openGLRenderWindow.setContainer(renderWindowContainer); + renderWindow.addView(openGLRenderWindow); + openGLRenderWindow.setSize(1, 1); + + renderWindow.render(); + + const shiftedCABOs = getActivePrimitiveCABOs(openGLRenderWindow, mapper); + t.ok(shiftedCABOs.length > 0, 'glyph source VBOs were built'); + t.ok( + shiftedCABOs.some((cabo) => cabo.getCoordShiftAndScaleEnabled()), + 'far glyph centers enable shift/scale on source VBOs' + ); + + updatePoints(centers, [-0.5, 0, 0, 0.5, 0, 0]); + renderWindow.render(); + + const unshiftedCABOs = getActivePrimitiveCABOs(openGLRenderWindow, mapper); + t.ok( + unshiftedCABOs.every((cabo) => !cabo.getCoordShiftAndScaleEnabled()), + 'source VBO shift/scale is cleared once glyph centers no longer use it' + ); + + gc.releaseResources(); + } +); diff --git a/Sources/Rendering/OpenGL/Glyph3DMapper/index.js b/Sources/Rendering/OpenGL/Glyph3DMapper/index.js index ae1205c333a..8c1c0776310 100644 --- a/Sources/Rendering/OpenGL/Glyph3DMapper/index.js +++ b/Sources/Rendering/OpenGL/Glyph3DMapper/index.js @@ -754,14 +754,16 @@ function vtkOpenGLGlyph3DMapper(publicAPI, model) { superClass.buildBufferObjects(ren, actor); - // apply shift + scale to primitives AFTER vtkOpenGLPolyDataMapper.buildBufferObjects - // so that the Glyph3DMapper gets the last say in the shift + scale - if (useShiftAndScale) { - for (let i = primTypes.Start; i < primTypes.End; i++) { - model.primitives[i] - .getCABO() - .setCoordShiftAndScale(coordShift, coordScale); - } + // Apply shift + scale to primitives AFTER vtkOpenGLPolyDataMapper.buildBufferObjects + // so that the Glyph3DMapper gets the last say in the shift + scale. Clear + // the previous frame's transform when the current glyph centers no longer + // need it; otherwise glyphs keep rendering with stale source-VBO scaling. + const sourceCoordShift = useShiftAndScale ? coordShift : null; + const sourceCoordScale = useShiftAndScale ? coordScale : null; + for (let i = primTypes.Start; i < primTypes.End; i++) { + model.primitives[i] + .getCABO() + .setCoordShiftAndScale(sourceCoordShift, sourceCoordScale); } }; }