Ejemplo n.º 1
0
def box(min=[0, 0, 0],
        max=[1, 1, 1],
        filled=False,
        color=QColor("blue"),
        effect_f=CustomEffects.material,
        matrix=np.eye(4, dtype='f4'),
        name="box"):

    color = ensure_QColor(color)

    indices = [0,1,3, 3,2,0,   7,5,4, 4,6,7,   4,5,1, 1,0,4,   5,7,3, 3,1,5,   6,2,3, 3,7,6,   4,0,2, 2,6,4] if filled else\
              [0,1, 1,3, 3,2, 2,0,       4,5, 5,7, 7,6, 6,4,      0,4, 1,5, 2,6, 3,7]

    vertices = [
        [min[0], min[1], min[2]]  #0
        ,
        [min[0], min[1], max[2]]  #1
        ,
        [min[0], max[1], min[2]]  #2
        ,
        [min[0], max[1], max[2]]  #3
        ,
        [max[0], min[1], min[2]]  #4
        ,
        [max[0], min[1], max[2]]  #5
        ,
        [max[0], max[1], min[2]]  #6
        ,
        [max[0], max[1], max[2]]  #7
    ]

    normals = [
        [-0.577350269, -0.577350269, -0.577350269]  #0
        ,
        [-0.577350269, -0.577350269, 0.577350269]  #1
        ,
        [-0.577350269, 0.577350269, -0.577350269]  #2
        ,
        [-0.577350269, 0.577350269, 0.577350269]  #3
        ,
        [0.577350269, -0.577350269, -0.577350269]  #4
        ,
        [0.577350269, -0.577350269, 0.577350269]  #5
        ,
        [0.577350269, 0.577350269, -0.577350269]  #6
        ,
        [0.577350269, 0.577350269, 0.577350269]  #7
    ]

    return Actors.Actor(
        geometry=Geometry.Geometry(
            indices=Array.Array(ndarray=np.array(indices, 'u4')),
            attribs=Geometry.Attribs(
                vertices=Array.Array(ndarray=np.array(vertices, 'f4')),
                normals=Array.Array(ndarray=np.array(normals, 'f4'))),
            primitive_type=Geometry.PrimitiveType.TRIANGLES
            if filled else Geometry.PrimitiveType.LINES),
        effect=effect_f(color) if filled else CustomEffects.emissive(color),
        transform=ensure_Transform(matrix),
        name=name)
Ejemplo n.º 2
0
    def __init__(self, parent=None):
        super(DasSampleToCloud, self).__init__(parent)
        self._sample = None
        self._seg3DSample = None
        self._referential = None
        self._undistort = False
        self._undistortRefTs = -1
        self._primitiveType = Geometry.PrimitiveType.POINTS

        self._indices = Array.ArrayUInt1()
        self._indices.set_producer(self)
        self._amplitudes = Array.ArrayFloat1()
        self._amplitudes.set_producer(self)

        self._colors = Array.ArrayFloat4()
        self._colors.set_producer(self)

        self._transform = Transforms.Transform()
        self._transform.set_producer(self)

        self._minAmplitude = np.nan
        self._maxAmplitude = np.nan
        self._hasReferential = True
        self._logScale = False

        self._method = None

        self._amplitudeRatio = 100

        #call setters:
        self.method = 'point_cloud'
        self.sample = Product.VariantProduct()
        self.seg3DSample = Product.VariantProduct()
Ejemplo n.º 3
0
def lines(indices,
          vertices,
          color=QColor("blue"),
          matrix=np.eye(4, dtype='f4'),
          name="lines"):
    color = ensure_QColor(color)
    return Actors.Actor(geometry=Geometry.Geometry(
        indices=Array.Array(ndarray=indices),
        attribs=Geometry.Attribs(vertices=Array.Array(ndarray=vertices)),
        primitive_type=Geometry.PrimitiveType.LINES),
                        effect=CustomEffects.emissive(color),
                        transform=ensure_Transform(matrix),
                        name=name)
Ejemplo n.º 4
0
def colored_point_cloud(points,
                        colors,
                        matrix=np.eye(4, dtype='f4'),
                        name="pcl"):
    #color = ensure_QColor(color)
    return Actors.Actor(geometry=Geometry.Geometry(
        attribs=CustomAttribs.ColorsAttribs(
            vertices=Array.Array(ndarray=points),
            colors=Array.Array(ndarray=colors)),
        primitive_type=Geometry.PrimitiveType.POINTS),
                        effect=CustomEffects.point_colors(),
                        transform=ensure_Transform(matrix),
                        name=name)
Ejemplo n.º 5
0
def quad(top_left=[0, 0, 0],
         bottom_left=[0, 1, 0],
         bottom_right=[1, 1, 0],
         top_right=None,
         texcoords=[[0, 0], [0, 1], [1, 1], [1, 0]],
         filled=False,
         color=QColor("blue"),
         effect_f=CustomEffects.material,
         textures=None,
         matrix=np.eye(4, dtype='f4'),
         name="quad"):

    color = ensure_QColor(color)

    top_left = utils.to_numpy(top_left)
    bottom_left = utils.to_numpy(bottom_left)
    bottom_right = utils.to_numpy(bottom_right)

    v_top = top_left - bottom_left
    v_right = bottom_right - bottom_left
    if top_right is None:
        top_right = bottom_right + v_top
    else:
        top_right = utils.to_numpy(top_right)

    indices = [0, 1, 2, 3, 0, 2
               ] if filled else [0, 1, 1, 2, 2, 0, 3, 0, 0, 2, 2, 3]

    vertices = np.vstack((top_left, bottom_left, bottom_right, top_right))

    normals = np.empty_like(vertices)

    n = np.cross(v_right, v_top)
    normals[:] = n / np.linalg.norm(n)

    if textures is not None:
        effect = effect_f(textures=textures, color=color)
    else:
        effect = effect_f(color=color)

    return Actors.Actor(geometry=Geometry.Geometry(
        indices=Array.Array(ndarray=np.array(indices, 'u4')),
        attribs=CustomAttribs.TexcoordsAttribs(
            vertices=Array.Array(ndarray=np.array(vertices, 'f4')),
            normals=Array.Array(ndarray=np.array(normals, 'f4')),
            texcoords0=Array.Array(ndarray=np.array(texcoords, 'f4'))),
        primitive_type=Geometry.PrimitiveType.TRIANGLES
        if filled else Geometry.PrimitiveType.LINES),
                        effect=effect,
                        transform=ensure_Transform(matrix),
                        name=name)
Ejemplo n.º 6
0
    def __init__(self, parent=None):

        super(TracesGeometry, self).__init__(parent)

        self.ndarray = np.empty((0, 2), dtype=np.float32)
        #inputs:
        self._traces = None
        self._specs = None

        #outputs:
        self._indices = Array.ArrayUInt1()
        self._indices.set_producer(self)

        self._saturationIndices = Array.ArrayUInt1()
        self._saturationIndices.set_producer(self)
Ejemplo n.º 7
0
def text(text,
         font="Arial",
         font_size=6,
         line_width=1,
         color=QColor("blue"),
         matrix=np.eye(4, dtype='f4'),
         is_billboard=True,
         name="text",
         scale=0.1,
         origin=[0, 0, 0],
         u=[1, 0, 0],
         v=[0, 1, 0],
         w=[0, 0, 1]):
    '''
        Warning, this function can crash if called before any call to QApplication(sys.argv)
    '''

    color = ensure_QColor(color)

    origin = utils.to_numpy(origin)
    u = utils.to_numpy(u)
    v = utils.to_numpy(v)
    w = utils.to_numpy(w)

    indices = []
    vertices = []

    path = QPainterPath()
    path.addText(QPointF(0, 0), QFont(font, font_size), text)
    polygons = path.toSubpathPolygons()
    for polygon in polygons:
        for point in polygon:
            indices.append(len(vertices))
            p = utils.to_numpy([point.x(), point.y(), 0]) * scale
            vertices.append(origin + p[0] * u + p[1] * v + p[2] * w)
        indices.append(-1)

    return Actors.Actor(geometry=Geometry.Geometry(
        indices=Array.Array(ndarray=np.array(indices, 'u4')),
        attribs=Geometry.Attribs(vertices=Array.Array(
            ndarray=np.array(vertices, 'f4'))),
        primitive_type=Geometry.PrimitiveType.LINE_LOOP),
                        effect=CustomEffects.emissive(
                            color,
                            line_width=line_width,
                            is_billboard=is_billboard),
                        transform=ensure_Transform(matrix),
                        name=f"{name}_{text}")
Ejemplo n.º 8
0
 def __init__(self, parent=None):
     super(ROSStereoCalibratorFilter, self).__init__(parent=parent)
     self._imageArrayRight = None
     self._images_right = []
     self._leftCalibPath = ""
     self._rightCalibPath = ""
     self._rightImageResult = Array.Array()
     self._rightImageResult.set_producer(self)
Ejemplo n.º 9
0
def from_mesh(mesh,
              color=QColor("blue"),
              effect_f=CustomEffects.material,
              scale=1,
              matrix=np.eye(4, dtype='f4'),
              name="mesh"):
    color = ensure_QColor(color)
    mesh.add_attribute("vertex_normal")
    return Actors.Actor(geometry=Geometry.Geometry(
        indices=Array.Array(ndarray=mesh.faces.flatten().astype('u4')),
        attribs=Geometry.Attribs(
            vertices=Array.Array(ndarray=mesh.vertices.astype('f4') * scale),
            normals=Array.Array(ndarray=mesh.get_vertex_attribute(
                "vertex_normal").astype('f4'))),
        primitive_type=Geometry.PrimitiveType.TRIANGLES),
                        effect=effect_f(color=color),
                        transform=ensure_Transform(matrix),
                        name=name)
Ejemplo n.º 10
0
    def __init__(self, parent=None):
        super(CloudBase, self).__init__(parent)
        self.ndarray = np.empty((0, 3), dtype=np.float32)
        #inputs:
        self._bankSelection = []  #empty means all
        self._packages = None
        self._specs = None

        #outputs:
        self._indices = Array.ArrayUInt1()
        self._indices.set_producer(self)

        self._minAmplitude = np.nan
        self._maxAmplitude = np.nan
        self._amplitudes = Array.ArrayFloat1()
        self._amplitudes.set_producer(self)

        self._banks = Array.ArrayUInt1()
        self._banks.set_producer(self)

        self._directions = None
        self._quad_directions = None
Ejemplo n.º 11
0
def axes(size=1, matrix=np.eye(4, dtype='f4'), line_width=1, name="axes"):

    indices = [0, 1, 2, 3, 4, 5]
    vertices = [
        [0, 0, 0]  #0
        ,
        [size, 0, 0]  #1
        ,
        [0, 0, 0]  #2
        ,
        [0, size, 0]  #3
        ,
        [0, 0, 0]  #4
        ,
        [0, 0, size]  #5
    ]
    colors = [
        [1, 0, 0]  #red
        ,
        [1, 0, 0]  #red
        ,
        [0, 1, 0]  #green
        ,
        [0, 1, 0]  #green
        ,
        [0, 0, 1]  #blue
        ,
        [0, 0, 1]  #blue
    ]
    return Actors.Actor(
        geometry=Geometry.Geometry(
            indices=Array.Array(ndarray=np.array(indices, 'u4')),
            attribs=CustomAttribs.ColorsAttribs(
                vertices=Array.Array(ndarray=np.array(vertices, 'f4')),
                colors=Array.Array(ndarray=np.array(colors, 'f4'))),
            primitive_type=Geometry.PrimitiveType.LINES),
        effect=CustomEffects.point_colors(line_width=line_width),
        transform=ensure_Transform(matrix),
        name=name)
Ejemplo n.º 12
0
def bbox(c=[0, 0, 0],
         d=[0, 0, 0],
         r=[0, 0, 0],
         color=QColor("blue"),
         effect_f=CustomEffects.material,
         matrix=np.eye(4, dtype='f4'),
         name="bbox",
         return_anchor=False,
         draw_orientation=True):

    color = ensure_QColor(color)

    indices = [
        0, 1, 1, 3, 3, 2, 2, 0, 4, 5, 5, 7, 7, 6, 6, 4, 0, 4, 1, 5, 2, 6, 3, 7
    ]
    vertices = linalg.bbox_to_8coordinates(c, d, r)
    vertices = np.vstack([vertices, np.mean(vertices[[5, 7]], axis=0)])

    if draw_orientation:
        # add central front point
        # and indices to draw the direction arrow
        indices += [1, 8, 3, 8]

    text_anchor = vertices[np.argmin(np.sum(vertices, axis=1))]
    vertices = vertices.astype('f4')

    actor = Actors.Actor(geometry=Geometry.Geometry(
        indices=Array.Array(ndarray=np.array(indices, 'u4')),
        attribs=Geometry.Attribs(vertices=Array.Array(ndarray=vertices)),
        primitive_type=Geometry.PrimitiveType.LINES),
                         effect=CustomEffects.emissive(color),
                         transform=ensure_Transform(matrix),
                         name=name)

    if return_anchor:
        return actor, vertices[-1]
    else:
        return actor
Ejemplo n.º 13
0
def colored_quad_cloud(points,
                       amplitude,
                       indices,
                       colormap="viridis",
                       log_scale=False,
                       cm_resolution=256,
                       matrix=np.eye(4, dtype='f4'),
                       color=None,
                       name="quad"):
    #color = ensure_QColor(color)

    min_amplitude = float(amplitude.min())
    max_amplitude = float(amplitude.max())

    if log_scale:
        norm = mpl_colors.LogNorm(1, cm_resolution)
    else:
        norm = mpl_colors.Normalize(0, cm_resolution)

    colormap_ = getattr(cm, colormap)(norm(np.arange(cm_resolution)))
    cm_array = np.ascontiguousarray(colormap_ * 255, dtype=np.uint8)

    if color is None:
        effect = CustomEffects.color_map(Array.Array(ndarray=cm_array),
                                         amplitude.min(), amplitude.max())
    else:
        effect = CustomEffects.material(color=color)

    return Actors.Actor(geometry=Geometry.Geometry(
        indices=Array.Array(ndarray=indices),
        attribs=CustomAttribs.ColorsAttribs(
            vertices=Array.Array(ndarray=points),
            colors=Array.Array(ndarray=amplitude)),
        primitive_type=Geometry.PrimitiveType.TRIANGLES),
                        effect=effect,
                        transform=ensure_Transform(matrix),
                        name=name)
Ejemplo n.º 14
0
def colormap_point_cloud(points,
                         amplitude,
                         min_amplitude=None,
                         max_amplitude=None,
                         colormap="viridis",
                         log_scale=False,
                         cm_resolution=256,
                         matrix=np.eye(4, dtype='f4'),
                         name="cmap_pcl"):
    """
    resolution: the color map texture resolution, it affect granularity/precision of the color distribution only
    """

    if min_amplitude is None:
        min_amplitude = float(amplitude.min())

    if max_amplitude is None:
        max_amplitude = float(amplitude.max())

    if log_scale:
        norm = mpl_colors.LogNorm(1, cm_resolution)
    else:
        norm = mpl_colors.Normalize(0, cm_resolution)

    colormap_ = getattr(cm, colormap)(norm(np.arange(cm_resolution)))
    cm_array = np.ascontiguousarray(colormap_ * 255, dtype=np.uint8)

    return Actors.Actor(geometry=Geometry.Geometry(
        attribs=CustomAttribs.AmplitudeAttribs(
            vertices=Array.Array(ndarray=points),
            amplitude=Array.Array(ndarray=amplitude)),
        primitive_type=Geometry.PrimitiveType.POINTS),
                        effect=CustomEffects.color_map(
                            Array.Array(ndarray=cm_array), min_amplitude,
                            max_amplitude),
                        transform=ensure_Transform(matrix),
                        name=name)
Ejemplo n.º 15
0
    def __init__(self):
        super(InFboRenderer, self).__init__()
        self.vertices_array = None
        self.sorted_actors = []
        self.vertex_array = None
        self.render_to_texture = None

        self.render_to_texture_attachment = -1
        self.render_to_texture_array = None
        self.locked_render_to_texture_array = Array.Array(
            ndarray=np.empty((0, 0, 4), np.uint8))

        self.draw_buffers = [
            GL.GL_COLOR_ATTACHMENT0, GL.GL_COLOR_ATTACHMENT1,
            GL.GL_COLOR_ATTACHMENT2, GL.GL_COLOR_ATTACHMENT3,
            GL.GL_COLOR_ATTACHMENT4
        ]
Ejemplo n.º 16
0
    def synchronize(self, viewport):

        # This function is called by Qt before calling render()
        # render() will then be called from another thread, which means anything
        # collected here (e.g. sorted_actors) should *not* be accessed during the rendering
        # I should map all buffers during synchronize, and copy all uniforms and whatnot

        viewport.setMirrorVertically(True)

        if self.vertex_array is None:
            # window = viewport.window()
            # gl_context = window.openglContext()
            # profile = QOpenGLVersionProfile()
            # profile.setProfile(QSurfaceFormat.CoreProfile)
            # profile.setVersion( 4, 1 )
            # self.gl = gl_context.versionFunctions(profile)

            self.vertex_array = QOpenGLVertexArrayObject()
            self.vertex_array.create()

        self.background_color = viewport._backgroundColor

        self.sorted_actors = []
        self.bo_actors = []
        self.out_textures = {}

        if viewport.actors is None:
            return

        sorted_actors = sorted(list(viewport.actors.get_visible_actors()),
                               key=attrgetter('renderRank'))
        sorted_actors.extend(viewport._debug_actors.get_visible_actors())

        for actor in sorted_actors:
            if not actor.update():
                if not hasattr(actor, "__error_reported___"
                               ) or not actor.__error_reported___:
                    LoggingManager.instance().warning("Not rendering actor " +
                                                      str(actor) +
                                                      ". It is has error " +
                                                      str(actor._error) + ".")
                    actor.__error_reported___ = True
                    actor.productDirty.connect(lambda actor=actor: setattr(
                        actor, "__error_reported___", False))
                continue

            if not hasattr(actor, 'bo_actor'):
                actor.bo_actor = {}
                actor.productDirty.connect(
                    lambda actor=actor: setattr(actor, "bo_actor", {}))

            if not actor.bo_actor:  # actor was dirty or is new

                indices = actor.geometry.indices
                attribs = actor.geometry.attribs.get_attributes()

                bo_actor = {
                    "attribs": {},
                    "textures": {},
                    "out_textures": {},
                    "uniforms":
                    copy.deepcopy(actor.effect.shader0.uniforms),
                    "indices":
                    None,
                    "program":
                    actor.effect.shader0._program,
                    "point_size":
                    actor.effect.pointSize,
                    "line_width":
                    actor.effect.lineWidth,
                    "transform":
                    actor.transform.worldTransform()
                    if actor.transform else QMatrix4x4(),
                    "primitiveType":
                    actor.geometry.primitiveType,
                    "actor_not_thread_safe":
                    actor
                }

                for name, value in viewitems(attribs):
                    if value is None:
                        continue
                    self.attach_buffer_object(value,
                                              QOpenGLBuffer.VertexBuffer)
                    value.___bo___.bind()

                    self.map_buffer_object(
                        value.___bo___,
                        value.ndarray.astype('f4') if value.ndarray.dtype
                        == np.float64 else value.ndarray)
                    value.___bo___.release()

                    bo_actor["attribs"][name] = value.___bo___

                for name, value in viewitems(actor.effect.shader0.textures):
                    if value is None:
                        LoggingManager.instance().warning(
                            'texture {} is null'.format(name))
                        continue
                    self.attach_texture_object(value)
                    if value.___tex___.target() != self.to_texture_target(
                            value):
                        LoggingManager.instance().warning(
                            'expected texture target  {}, got  {}'.format(
                                value.___tex___.target(),
                                self.to_texture_target(array)))
                        value.___tex___ = None
                        self.attach_texture_object(value)
                    if value.___tex___.dirty:

                        tex_ndarray = value.ndarray

                        if value.___tex___.target() == QOpenGLTexture.Target1D:
                            value.___tex___.setSize(tex_ndarray.shape[0])
                        if value.___tex___.target() == QOpenGLTexture.Target2D:
                            value.___tex___.setSize(tex_ndarray.shape[0],
                                                    tex_ndarray.shape[1])
                        if value.___tex___.target() == QOpenGLTexture.Target3D:
                            value.___tex___.setSize(tex_ndarray.shape[0],
                                                    tex_ndarray.shape[1],
                                                    tex_ndarray.shape[2])

                        value.___tex___.setFormat(QOpenGLTexture.RGBA8_UNorm)
                        value.___tex___.allocateStorage()
                        value.___tex___.setData(QOpenGLTexture.RGBA,
                                                QOpenGLTexture.UInt8,
                                                tex_ndarray.data)

                        value.___tex___.shape = value.ndarray.shape
                        value.___tex___.dtype = value.ndarray.dtype

                        value.___tex___.dirty = False

                    bo_actor["textures"][name] = value.___tex___

                for name, value in viewitems(
                        actor.effect.shader0.outputTextures):
                    if value is None:
                        LoggingManager.instance().warning(
                            f'output texture {name} is null')
                        continue

                    self.goc_output_texture(value)

                    bo_actor["out_textures"][name] = value.___tex___
                    self.out_textures[value.___tex___] = None

                if indices is not None and indices.size > 0:
                    self.attach_buffer_object(indices,
                                              QOpenGLBuffer.IndexBuffer)
                    indices.___bo___.bind()
                    self.map_buffer_object(indices.___bo___, indices.ndarray)
                    indices.___bo___.release()
                    bo_actor["indices"] = indices.___bo___

                actor.bo_actor = bo_actor

            self.sorted_actors.append(actor)
            self.bo_actors.append(actor.bo_actor)

        self.view_matrix = viewport.view_matrix()
        self.perspective_matrix = viewport.perspective_matrix()
        self.orthographic_matrix = viewport.orthographic_matrix()

        self.viewport_width = int(viewport.width())
        self.viewport_height = int(viewport.height())

        if viewport.render_to_texture_attachment != -1:

            old = self.render_to_texture_array
            self.render_to_texture_array = self.locked_render_to_texture_array
            self.locked_render_to_texture_array = old if old is not None else Array.Array(
                ndarray=np.empty((0, 0, 4), np.uint8))

            if self.render_to_texture_attachment != viewport.render_to_texture_attachment:
                self.del_render_to_texture_attachment()
                self.render_to_texture_attachment = viewport.render_to_texture_attachment

            self.goc_output_texture(self.locked_render_to_texture_array)
Ejemplo n.º 17
0
def load_collada(filename,
                 scale=1,
                 matrix=np.eye(4, dtype='f4'),
                 name="collada",
                 bake_matrix=True,
                 merge_actors=True,
                 ignore_non_textured=False,
                 invert_normals=False,
                 type_id=-1,
                 instance_id=-1):

    actors = Actors.Actors(shared_transform=ensure_Transform(
        np.eye(4, dtype='f4')) if bake_matrix else ensure_Transform(matrix),
                           name=name,
                           type_id=type_id,
                           instance_id=instance_id)

    mesh = collada.Collada(filename)

    np_matrix = utils.to_numpy(matrix)

    textures_cache = {}

    actors_cache = {}

    bbox = np.full((2, 3), np.finfo('f4').max)
    bbox[1, :] = np.finfo('f4').min

    actors.all_vertices = []
    actors.scale = scale

    for coll_geom in tqdm.tqdm(mesh.scene.objects('geometry')):
        for coll_prim in coll_geom.primitives():

            #FIXME: stop ignoring colladas transforms

            if isinstance(coll_prim, collada.triangleset.BoundTriangleSet):
                triangles = coll_prim
            elif isinstance(coll_prim, collada.polylist.BoundPolylist):
                triangles = coll_prim.triangleset()
            else:
                LoggingManager.instance().warning(
                    f"{type(coll_prim)} not implementend")
                continue

            textures = {}
            effect_signature = []  #for merging actors
            uniforms = {}
            for effect_name in triangles.material.effect.supported:
                value = getattr(triangles.material.effect, effect_name)
                if isinstance(value, collada.material.Map):

                    texture_image = value.sampler.surface.image
                    effect_signature.append((effect_name, texture_image.id))
                    if texture_image.id in textures_cache:
                        textures[effect_name] = textures_cache[
                            texture_image.id]
                    else:
                        array = textures[effect_name] = textures_cache[
                            texture_image.id] = Array.Array(
                                ndarray=utils.load_texture(
                                    texture_image.pilimage))
                elif isinstance(value, tuple):
                    uniforms[effect_name] = QColor.fromRgbF(*value)
                    effect_signature.append((effect_name, value))
                elif isinstance(value, float):
                    uniforms[effect_name] = value
                    effect_signature.append((effect_name, value))
                elif value is not None:
                    LoggingManager.instance().warning(
                        f"Unsupported type {effect_name}: {type(value)}")

            if not textures and ignore_non_textured:
                continue

            effect_signature = frozenset(effect_signature)

            triangles.generateNormals()

            vertices = triangles.vertex.astype('f4') * scale

            normals = triangles.normal.astype('f4')

            if invert_normals:
                normals = normals * -1

            if bake_matrix:
                vertices = linalg.map_points(np_matrix, vertices)
                normals = linalg.map_vectors(np_matrix, normals)

            indices = triangles.vertex_index.flatten().astype('u4')
            attributes_ndarrays = {"vertices": vertices, "normals": normals}
            indexed_vertices = vertices[triangles.vertex_index.flatten()]

            for i in range(3):
                bbox[0, i] = min(bbox[0, i], indexed_vertices[:, i].min())
                bbox[1, i] = max(bbox[1, i], indexed_vertices[:, i].max())

            if textures:
                if len(triangles.texcoordset) > 1:
                    LoggingManager.instance().warning(
                        f"warning, {type(coll_prim)} not implementend")
                orig_tc0 = triangles.texcoordset[0].astype('f4')
                tc0_idx = triangles.texcoord_indexset[0].flatten()
                if not np.all(tc0_idx == indices):
                    assert tc0_idx.shape == indices.shape, "texcoord indices must be the same shape as vertex indices"
                    #this will duplicate shared vertices so that we can have a separate texcoords for each triangle sharing vertices
                    attributes_ndarrays['vertices'] = indexed_vertices
                    attributes_ndarrays['normals'] = normals[
                        triangles.normal_index.flatten()]
                    indices = np.arange(indices.shape[0], dtype=indices.dtype)
                    uv = orig_tc0[tc0_idx]
                else:
                    uv = np.empty((vertices.shape[0], 2), 'f4')
                    uv[indices] = orig_tc0[tc0_idx]

                attributes_ndarrays['texcoords0'] = uv

                attribs = CustomAttribs.TexcoordsAttribs(
                    vertices=Array.Array(
                        ndarray=attributes_ndarrays['vertices']),
                    normals=Array.Array(
                        ndarray=attributes_ndarrays['normals']),
                    texcoords0=Array.Array(
                        ndarray=attributes_ndarrays['texcoords0']))
                #FIXME: bind collada uniforms if present
                effect = CustomEffects.textured_material(textures)
            else:
                attribs = Geometry.Attribs(
                    vertices=Array.Array(
                        ndarray=attributes_ndarrays['vertices']),
                    normals=Array.Array(
                        ndarray=attributes_ndarrays['normals']))
                #FIXME: bind other uniforms if present
                effect = CustomEffects.material(color=uniforms['diffuse'],
                                                back_color=uniforms['diffuse'])

            if invert_normals:
                indices = indices.reshape((indices.shape[0] // 3),
                                          3)[:, [0, 2, 1]].flatten()

            if merge_actors and effect_signature in actors_cache:

                actor = actors_cache[effect_signature]

                actor_attributes = actor.geometry.attribs.get_attributes()

                n_vertices_before = actor_attributes['vertices'].shape[0]

                for attr_name, value in actor_attributes.items():
                    value.set_ndarray(
                        np.vstack(
                            (value.ndarray, attributes_ndarrays[attr_name])))

                actor.geometry.indices.set_ndarray(
                    np.hstack((actor.geometry.indices.ndarray,
                               indices + n_vertices_before)))

            else:
                geometry = Geometry.Geometry(
                    indices=Array.Array(ndarray=indices), attribs=attribs)

                actor = actors.addActor(
                    Actors.Actor(geometry=geometry,
                                 effect=effect,
                                 transform=actors.shared_transform,
                                 name=f"{name}_{coll_geom.original.id}",
                                 type_id=type_id,
                                 instance_id=instance_id))

                actors_cache[effect_signature] = actor

                actors.all_vertices.append(
                    actor.geometry.attribs.vertices
                )  #if in merge actor mode, vertices are already there

    actors.bbox = bbox

    return actors