def _export_gltf(self, folder: str, name: str) -> str: """Export a scene to a glTF file. Args: folder: A valid path to where you'd like to write the gltf file. name: Name of the gltf file as a text string. Returns: A text string representing the path to the gltf file. """ # TODO: Find out why model's displaymode is not applied # TODO: Find out if the axis should be rotated to work with gltf viewers # TODO: Find a viewer for gltf files. The up axis of f3d viewer is Y axis. window, renderer = self._create_window()[1:] gltf_file_path = pathlib.Path(folder, f'{name}.gltf') exporter = vtk.vtkGLTFExporter() exporter.SaveNormalOn() exporter.InlineDataOn() exporter.SetFileName(gltf_file_path.as_posix()) exporter.SetActiveRenderer(renderer) exporter.SetRenderWindow(window) exporter.Modified() exporter.Write() return gltf_file_path.as_posix()
def ply2gltf(inname, outname): reader = vtk.vtkPLYReader() reader.SetFileName(inname) mapper = vtk.vtkPolyDataMapper() mapper.SetInputConnection(reader.GetOutputPort()) actor = vtk.vtkActor() actor.SetMapper(mapper) # Create a rendering window and renderer ren = vtk.vtkRenderer() renWin = vtk.vtkRenderWindow() renWin.AddRenderer(ren) # Assign actor to the renderer ren.AddActor(actor) # Export the GLTF writer = vtk.vtkGLTFExporter() writer.SetFileName(outname) writer.InlineDataOn() writer.SetRenderWindow(renWin) writer.Write()
def export(p, basename): #Export a single mesh to glb format. if os.path.exists('export/'): shutil.rmtree("export/") os.makedirs("export/") exp = vtk.vtkGLTFExporter() exp.SetInput(p.ren_win) exp.SetActiveRenderer(p.renderer) exp.SetFileName("export/{}.gltf".format(basename)) exp.Write() p.close() gltf2glb("export/{}.gltf".format(basename)) shutil.move("export/{}.glb".format(basename), "{}.glb".format(basename)) shutil.rmtree("export/") return True
triangulation.GetProperty().SetOpacity(1) else: triangulation = vtk.vtkActor() mapper = vtk.vtkPolyDataMapper() cleaner = vtk.vtkCleanPolyData() cleaner.SetInputConnection(meshReader.GetOutputPort()) mapper.SetInputConnection(cleaner.GetOutputPort()) triangulation.SetMapper(mapper) triangulation.GetProperty().SetOpacity(1) renderer = vtk.vtkRenderer() renderer.AddActor(triangulation) renderer.SetBackground(colors.GetColor3d("Black")) renderWindow = vtk.vtkRenderWindow() renderWindow.AddRenderer(renderer) renderWindow.SetWindowName("TexturedSphere") renWinInteractor = vtk.vtkRenderWindowInteractor() renWinInteractor.SetRenderWindow(renderWindow) renderWindow.Render() writer = vtk.vtkGLTFExporter() writer.SetFileName("bum.gltf") writer.InlineDataOn() writer.SetRenderWindow(renderWindow) writer.Write() renWinInteractor.Start()
def main(argv): if (len(argv) < 2): print("Usage: GLTFExporter file.gltf") colors = vtk.vtkNamedColors() # Set the background color. backgroundColor = colors.GetColor3d("SlateGray") # Create an arrow. arrowSource = vtk.vtkArrowSource() # Generate a random start and end point rng = vtk.vtkMinimalStandardRandomSequence() rng.SetSeed(8775070) # For testing. startPoint = [0,0,0] endPoint = [0,0,0] for i in range(3): rng.Next() startPoint[i] = rng.GetRangeValue(-10, 10) rng.Next() endPoint[i] = rng.GetRangeValue(-10, 10) # Compute a basis normalizedX = [0,0,0] normalizedY = [0,0,0] normalizedZ = [0,0,0] # The X axis is a vector from start to end vtk.vtkMath.Subtract(endPoint, startPoint, normalizedX) length = vtk.vtkMath.Norm(normalizedX) vtk.vtkMath.Normalize(normalizedX) # The Z axis is an arbitrary vector cross X arbitrary = [0,0,0] for i in range(3): rng.Next() arbitrary[i] = rng.GetRangeValue(-10, 10) vtk.vtkMath.Cross(normalizedX, arbitrary, normalizedZ) vtk.vtkMath.Normalize(normalizedZ) # The Y axis is Z cross X vtk.vtkMath.Cross(normalizedZ, normalizedX, normalizedY) matrix = vtk.vtkMatrix4x4() # Create the direction cosine matrix matrix.Identity() for i in range(3): matrix.SetElement(i, 0, normalizedX[i]) matrix.SetElement(i, 1, normalizedY[i]) matrix.SetElement(i, 2, normalizedZ[i]) # Apply the transforms transform = vtk.vtkTransform() transform.Translate(startPoint) transform.Concatenate(matrix) transform.Scale(length, length, length) # Transform the polydata transformPD = vtk.vtkTransformPolyDataFilter() transformPD.SetTransform(transform) transformPD.SetInputConnection(arrowSource.GetOutputPort()) # Create a mapper and actor for the arrow mapper = vtk.vtkPolyDataMapper() actor = vtk.vtkActor() if (USER_MATRIX): mapper.SetInputConnection(arrowSource.GetOutputPort()) actor.SetUserMatrix(transform.GetMatrix()) else: mapper.SetInputConnection(transformPD.GetOutputPort()) actor.SetMapper(mapper) actor.GetProperty().SetColor(colors.GetColor3d("Cyan")) # Create spheres for start and end point sphereStartSource = vtk.vtkSphereSource() sphereStartSource.SetCenter(startPoint) sphereStartSource.SetRadius(0.8) sphereStartMapper = vtk.vtkPolyDataMapper() sphereStartMapper.SetInputConnection(sphereStartSource.GetOutputPort()) sphereStart = vtk.vtkActor() sphereStart.SetMapper(sphereStartMapper) sphereStart.GetProperty().SetColor(colors.GetColor3d("Yellow")) sphereEndSource = vtk.vtkSphereSource() sphereEndSource.SetCenter(endPoint) sphereEndSource.SetRadius(0.8) sphereEndMapper = vtk.vtkPolyDataMapper() sphereEndMapper.SetInputConnection(sphereEndSource.GetOutputPort()) sphereEnd = vtk.vtkActor() sphereEnd.SetMapper(sphereEndMapper) sphereEnd.GetProperty().SetColor(colors.GetColor3d("Magenta")) # Create a renderer, render window, and interactor renderer = vtk.vtkRenderer() renderWindow = vtk.vtkRenderWindow() renderWindow.AddRenderer(renderer) renderWindow.SetSize(640, 480) renderWindow.SetWindowName("Oriented Arrow") style = vtk.vtkInteractorStyleTrackballCamera() renderWindowInteractor = vtk.vtkRenderWindowInteractor() renderWindowInteractor.SetRenderWindow(renderWindow) renderWindowInteractor.SetInteractorStyle(style) # Add the actor to the scene renderer.AddActor(actor) renderer.AddActor(sphereStart) renderer.AddActor(sphereEnd) renderer.SetBackground(backgroundColor) # Render and interact renderWindow.Render() renderer.GetActiveCamera().Azimuth(30) renderer.GetActiveCamera().Elevation(30) renderer.GetActiveCamera().Roll(30) renderer.ResetCameraClippingRange() renderWindowInteractor.Start() writer = vtk.vtkGLTFExporter() writer.SetFileName(argv[1]) writer.InlineDataOn() writer.SetRenderWindow(renderWindow) writer.Write() return 0
def exportModel(self, inputItem, outputFolder=None, reductionFactor=None, outputFormat=None): if outputFormat is None: outputFormat = "glTF" if reductionFactor is not None: self.reductionFactor = reductionFactor self._exportToFile = (outputFormat != "scene") if outputFolder is None: if self._exportToFile: raise ValueError( "Output folder must be specified if output format is not 'scene'" ) shNode = slicer.vtkMRMLSubjectHierarchyNode.GetSubjectHierarchyNode( slicer.mrmlScene) inputName = shNode.GetItemName(inputItem) # Get input as a subject hierarchy folder owner = shNode.GetItemOwnerPluginName(inputItem) if owner == "Folder": # Input is already a model hiearachy inputShFolderItemId = inputItem self._outputShFolderItemId = shNode.CreateFolderItem( shNode.GetSceneItemID(), inputName + " export") elif owner == "Segmentations": # Export segmentation to model hierarchy segLogic = slicer.modules.segmentations.logic() folderName = inputName + '_Models' inputShFolderItemId = shNode.CreateFolderItem( shNode.GetSceneItemID(), folderName) inputSegmentationNode = shNode.GetItemDataNode(inputItem) self.addLog( 'Export segmentation to models. This may take a few minutes.') success = segLogic.ExportAllSegmentsToModels( inputSegmentationNode, inputShFolderItemId) self._outputShFolderItemId = inputShFolderItemId else: raise ValueError( "Input item must be a segmentation node or a folder containing model nodes" ) modelNodes = vtk.vtkCollection() shNode.GetDataNodesInBranch(inputShFolderItemId, modelNodes, "vtkMRMLModelNode") self._numberOfExpectedModels = modelNodes.GetNumberOfItems() self._numberOfProcessedModels = 0 self._gltfNodes = [] self._gltfMeshes = [] # Add models to a self._renderer self.addModelsToRenderer(inputShFolderItemId, boostGouraudColor=(outputFormat == "glTF")) if self._exportToFile: outputFileName = inputName # import datetime # dateTimeStr = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") # outputFileName += dateTimeStr outputFilePathBase = os.path.join(outputFolder, outputFileName) if outputFormat == "glTF": exporter = vtk.vtkGLTFExporter() outputFilePath = outputFilePathBase + '.gltf' exporter.SetFileName(outputFilePath) exporter.InlineDataOn() # save to single file exporter.SaveNormalOn() # save surface normals elif outputFormat == "OBJ": exporter = vtk.vtkOBJExporter() outputFilePath = outputFilePathBase + '.obj' exporter.SetFilePrefix(outputFilePathBase) else: raise ValueError("Output format must be scene, glTF, or OBJ") self.addLog(f"Writing file {outputFilePath}...") exporter.SetRenderWindow(self._renderWindow) exporter.Write() if outputFormat == "glTF": # Fix up the VTK-generated glTF file import json with open(outputFilePath, 'r') as f: jsonData = json.load(f) # Update mesh names for meshIndex, mesh in enumerate(self._gltfMeshes): jsonData['meshes'][meshIndex]['name'] = mesh['name'] # VTK uses "OPAQUE" alpha mode for all meshes, which would make all nodes appear opaque. # Replace alpha mode by "BLEND" for semi-transparent meshes. for material in jsonData['materials']: rgbaColor = material['pbrMetallicRoughness'][ 'baseColorFactor'] if rgbaColor[3] < 1.0: material['alphaMode'] = 'BLEND' # Add camera nodes from the VTK-exported file for node in enumerate(self._gltfNodes): if 'camera' in node: self._gltfNodes.append(node) # Replace the entire hierarchy jsonData['nodes'] = self._gltfNodes # Set up root node rootNodeIndex = len(self._gltfNodes) - 1 # View up direction in glTF is +Y. # We map that to anatomical S direction by this transform (LSA coordinate system). jsonData['nodes'][rootNodeIndex]['matrix'] = [ 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] # The scene root is the last node in the self._gltfNodes list jsonData['scenes'][0]['nodes'] = [rootNodeIndex] jsonData['asset'][ 'generator'] = f"{slicer.app.applicationName} {slicer.app.applicationVersion}" with open(outputFilePath, 'w') as f: f.write(json.dumps(jsonData, indent=3)) # TODO: # - Add scene view states as scenes # - Add option to change up vector (glTF defines the y axis as up, https://github.com/KhronosGroup/glTF/issues/1043 # https://castle-engine.io/manual_up.php) # # Preview # iren = vtk.vtkRenderWindowInteractor() # iren.SetRenderWindow(renderWindow) # iren.Initialize() # renderer.ResetCamera() # renderer.GetActiveCamera().Zoom(1.5) # renderWindow.Render() # iren.Start() # Remove temporary nodes for node in self._temporaryExportNodes: slicer.mrmlScene.RemoveNode(node) self._temporaryExportNodes = [] self._numberOfExpectedModels = 0 self._numberOfProcessedModels = 0 self._renderer = None self._renderWindow = None self._decimationParameterNode = None if self._exportToFile: shNode.RemoveItem(self._outputShFolderItemId)