Exemple #1
0
    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()
Exemple #2
0
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()
Exemple #3
0
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
Exemple #4
0
    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()
Exemple #5
0
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)