|
| 1 | +import '@kitware/vtk.js/favicon'; |
| 2 | + |
| 3 | +// Load the rendering pieces we want to use (for both WebGL and WebGPU) |
| 4 | +import '@kitware/vtk.js/Rendering/Profiles/Geometry'; |
| 5 | + |
| 6 | +import '@kitware/vtk.js/IO/Core/DataAccessHelper/HttpDataAccessHelper'; // HTTP + zip |
| 7 | + |
| 8 | +import macro from '@kitware/vtk.js/macros'; |
| 9 | +import vtkDataArray from '@kitware/vtk.js/Common/Core/DataArray'; |
| 10 | +import vtkPolyData from '@kitware/vtk.js/Common/DataModel/PolyData'; |
| 11 | +import vtkFullScreenRenderWindow from '@kitware/vtk.js/Rendering/Misc/FullScreenRenderWindow'; |
| 12 | +import vtkActor from '@kitware/vtk.js/Rendering/Core/Actor'; |
| 13 | +import vtkMapper from '@kitware/vtk.js/Rendering/Core/Mapper'; |
| 14 | +import vtkSphereSource from '@kitware/vtk.js/Filters/Sources/SphereSource'; |
| 15 | +import vtkTexture from '@kitware/vtk.js/Rendering/Core/Texture'; |
| 16 | +import vtkImageGridSource from '@kitware/vtk.js/Filters/Sources/ImageGridSource'; |
| 17 | +import vtkOBJWriter from '@kitware/vtk.js/IO/Misc/OBJWriter'; |
| 18 | + |
| 19 | +// ---------------------------------------------------------------------------- |
| 20 | +// Standard rendering code setup |
| 21 | +// ---------------------------------------------------------------------------- |
| 22 | + |
| 23 | +const fullScreenRenderer = vtkFullScreenRenderWindow.newInstance(); |
| 24 | +const renderer = fullScreenRenderer.getRenderer(); |
| 25 | +const renderWindow = fullScreenRenderer.getRenderWindow(); |
| 26 | + |
| 27 | +const resetCamera = renderer.resetCamera; |
| 28 | +const render = renderWindow.render; |
| 29 | + |
| 30 | +const gridSource = vtkImageGridSource.newInstance(); |
| 31 | +gridSource.setDataExtent(0, 511, 0, 511, 0, 0); |
| 32 | +gridSource.setGridSpacing(16, 16, 0); |
| 33 | +gridSource.setGridOrigin(8, 8, 0); |
| 34 | + |
| 35 | +const texture = vtkTexture.newInstance(); |
| 36 | +texture.setInterpolate(true); |
| 37 | +texture.setInputConnection(gridSource.getOutputPort()); |
| 38 | + |
| 39 | +const sphereSource = vtkSphereSource.newInstance(); |
| 40 | +sphereSource.setThetaResolution(64); |
| 41 | +sphereSource.setPhiResolution(32); |
| 42 | + |
| 43 | +const actor = vtkActor.newInstance(); |
| 44 | +const mapper = vtkMapper.newInstance(); |
| 45 | + |
| 46 | +// create a filter on the fly to generate tcoords from normals |
| 47 | +const tcoordFilter = macro.newInstance((publicAPI, model) => { |
| 48 | + macro.obj(publicAPI, model); // make it an object |
| 49 | + macro.algo(publicAPI, model, 1, 1); // mixin algorithm code 1 in, 1 out |
| 50 | + publicAPI.requestData = (inData, outData) => { |
| 51 | + // implement requestData |
| 52 | + if (!outData[0] || inData[0].getMTime() > outData[0].getMTime()) { |
| 53 | + // use the normals to generate tcoords :-) |
| 54 | + const norms = inData[0].getPointData().getNormals(); |
| 55 | + |
| 56 | + const newArray = new Float32Array(norms.getNumberOfTuples() * 2); |
| 57 | + const ndata = norms.getData(); |
| 58 | + for (let i = 0; i < newArray.length; i += 2) { |
| 59 | + newArray[i] = |
| 60 | + Math.abs(Math.atan2(ndata[(i / 2) * 3], ndata[(i / 2) * 3 + 1])) / |
| 61 | + 3.1415927; |
| 62 | + newArray[i + 1] = Math.asin(ndata[(i / 2) * 3 + 2] / 3.1415927) + 0.5; |
| 63 | + } |
| 64 | + |
| 65 | + const da = vtkDataArray.newInstance({ |
| 66 | + numberOfComponents: 2, |
| 67 | + values: newArray, |
| 68 | + }); |
| 69 | + da.setName('tcoord'); |
| 70 | + |
| 71 | + const pd = vtkPolyData.newInstance(); |
| 72 | + pd.setPolys(inData[0].getPolys()); |
| 73 | + pd.setPoints(inData[0].getPoints()); |
| 74 | + const cpd = pd.getPointData(); |
| 75 | + cpd.addArray(da); |
| 76 | + cpd.setActiveTCoords(da.getName()); |
| 77 | + outData[0] = pd; |
| 78 | + } |
| 79 | + }; |
| 80 | +})(); |
| 81 | + |
| 82 | +tcoordFilter.setInputConnection(sphereSource.getOutputPort()); |
| 83 | +mapper.setInputConnection(tcoordFilter.getOutputPort()); |
| 84 | + |
| 85 | +actor.setMapper(mapper); |
| 86 | +actor.addTexture(texture); |
| 87 | + |
| 88 | +renderer.addActor(actor); |
| 89 | +resetCamera(); |
| 90 | +render(); |
| 91 | + |
| 92 | +const writer = vtkOBJWriter.newInstance({ |
| 93 | + textureFileName: 'grid.png', |
| 94 | +}); |
| 95 | +console.log(writer); |
| 96 | + |
| 97 | +writer.setInputData(sphereSource.getOutputData(), 0); |
| 98 | +writer.setInputData(gridSource.getOutputData(), 1); |
| 99 | +writer.setTextureFileName('grid.png'); |
| 100 | + |
| 101 | +// const objContent = writer.getOutputData(); |
| 102 | +// const mtlContent = writer.getMtl(); |
| 103 | + |
| 104 | +const zip = writer.exportAsZIP(); |
| 105 | + |
| 106 | +// Can also use a static function to write to OBJ: |
| 107 | +// const fileContents = vtkOBJWriter.writeOBJ(reader.getOutputData()); |
| 108 | + |
| 109 | +// Add a download link for it |
| 110 | +const blob = new Blob([zip], { |
| 111 | + type: 'application/octet-steam', |
| 112 | +}); |
| 113 | +const a = window.document.createElement('a'); |
| 114 | +a.href = window.URL.createObjectURL(blob, { |
| 115 | + type: 'application/zip', |
| 116 | +}); |
| 117 | +a.download = 'sphere.zip'; |
| 118 | +a.text = 'Download'; |
| 119 | +a.style.position = 'absolute'; |
| 120 | +a.style.left = '50%'; |
| 121 | +a.style.bottom = '10px'; |
| 122 | +document.body.appendChild(a); |
| 123 | +a.style.background = 'white'; |
| 124 | +a.style.padding = '5px'; |
| 125 | + |
| 126 | +global.writer = writer; |
| 127 | +global.fullScreenRenderer = fullScreenRenderer; |
0 commit comments