Exemplo n.º 1
0
    def load(self) -> Scene:
        """Loads and stl scene/file

        Returns:
            Scene: The Scene instance
        """
        path = self.find_scene(self.meta.path)
        if not path:
            raise ImproperlyConfigured("Scene '{}' not found".format(
                self.meta.path))

        file_obj = str(path)
        if file_obj.endswith('.gz'):
            file_obj = gzip.GzipFile(file_obj)

        stl_mesh = trimesh.load(file_obj, file_type='stl')
        scene = Scene(self.meta.resolved_path)
        scene_mesh = Mesh("mesh")
        scene_mesh.material = Material("default")

        vao = VAO("mesh", mode=moderngl.TRIANGLES)
        vao.buffer(numpy.array(stl_mesh.vertices, dtype='f4'), '3f',
                   ['in_position'])
        vao.buffer(numpy.array(stl_mesh.vertex_normals, dtype='f4'), '3f',
                   ['in_normal'])
        vao.index_buffer(numpy.array(stl_mesh.faces, dtype='u4'))
        scene_mesh.vao = vao
        scene_mesh.add_attribute('POSITION', 'in_position', 3)
        scene_mesh.add_attribute('NORMAL', 'in_normal', 3)

        scene.meshes.append(scene_mesh)
        scene.root_nodes.append(Node(mesh=scene_mesh))
        scene.prepare()

        return scene
Exemplo n.º 2
0
    def load(self, materials):
        name_map = {
            'POSITION': self.meta.attr_names.POSITION,
            'NORMAL': self.meta.attr_names.NORMAL,
            'TEXCOORD_0': self.meta.attr_names.TEXCOORD_0,
            'TANGENT': self.meta.attr_names.TANGENT,
            'JOINTS_0': self.meta.attr_names.JOINTS_0,
            'WEIGHTS_0': self.meta.attr_names.WEIGHTS_0,
            'COLOR_0': self.meta.attr_names.COLOR_0,
        }

        meshes = []

        # Read all primitives as separate meshes for now
        # According to the spec they can have different materials and vertex format
        for primitive in self.primitives:

            vao = VAO(self.name, mode=primitive.mode or moderngl.TRIANGLES)

            # Index buffer
            component_type, index_vbo = self.load_indices(primitive)
            if index_vbo is not None:
                vao.index_buffer(
                    moderngl_window.ctx().buffer(index_vbo.tobytes()),
                    index_element_size=component_type.size,
                )

            attributes = {}
            vbos = self.prepare_attrib_mapping(primitive)

            for vbo_info in vbos:
                dtype, buffer = vbo_info.create()
                vao.buffer(
                    buffer,
                    " ".join(["{}{}".format(attr[1], DTYPE_BUFFER_TYPE[dtype]) for attr in vbo_info.attributes]),
                    [name_map[attr[0]] for attr in vbo_info.attributes],
                )

                for attr in vbo_info.attributes:
                    attributes[attr[0]] = {
                        'name': name_map[attr[0]],
                        'components': attr[1],
                        'type': vbo_info.component_type.value,
                    }

            bbox_min, bbox_max = self.get_bbox(primitive)
            meshes.append(Mesh(
                self.name, vao=vao, attributes=attributes,
                material=materials[primitive.material] if primitive.material is not None else None,
                bbox_min=bbox_min, bbox_max=bbox_max,
            ))

        return meshes
Exemplo n.º 3
0
class Terrain(mglw.WindowConfig):
    """"""
    title = "Terrain"
    resource_dir = (Path(__file__) / '../resources').absolute()
    aspect_ratio = None
    window_size = 1280, 720
    resizable = True
    samples = 16

    clear_color = (51 / 255, 51 / 255, 51 / 255)

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.camera = KeyboardCamera(self.wnd.keys,
                                     aspect_ratio=self.wnd.aspect_ratio)
        self.terrain_program = self.load_program('my_shader.glsl')
        self.ctx.front_face = 'cw'

        # self.ctx.wireframe = True
        self.translate = Matrix44.from_translation((0, 1., .25))
        self.rotate = Matrix44.look_at((0., 1., .25), (0., 0., -1.),
                                       (0., 1., 0.))
        self.projection = (self.camera.projection.matrix *
                           (self.translate * self.rotate)).astype('f4')
        self.terrain_program['projection'].write(self.projection.tobytes())

        terrain_resolution = 5

        points = generate(terrain_resolution).astype('f4')
        self.buffer = self.ctx.buffer(points)

        indices = generate_index_buffer(terrain_resolution).astype('i4')
        print(indices)
        self.index_buffer = self.ctx.buffer(indices)

        self.vao_1 = VAO(name='vao_1')
        self.vao_1.buffer(self.buffer, '3f', ['in_position'])
        self.vao_1.index_buffer(self.index_buffer)

    def render(self, time, frame_time):
        self.ctx.clear(*self.clear_color)
        # self.ctx.clear(0.)
        self.ctx.enable_only(moderngl.DEPTH_TEST | moderngl.CULL_FACE)
        self.vao_1.render(self.terrain_program, mode=moderngl.POINTS)

    def resize(self, width: int, height: int):
        self.camera.projection.update(aspect_ratio=self.wnd.aspect_ratio)
Exemplo n.º 4
0
def sphere(
    radius=0.5,
    sectors=32,
    rings=16,
    normals=True,
    uvs=True,
    name: str = None,
    attr_names=AttributeNames,
) -> VAO:
    """Creates a sphere.

    Keyword Args:
        radius (float): Radius or the sphere
        rings (int): number or horizontal rings
        sectors (int): number of vertical segments
        normals (bool): Include normals in the VAO
        uvs (bool): Include texture coordinates in the VAO
        name (str): An optional name for the VAO
        attr_names (AttributeNames): Attribute names
    Returns:
        A :py:class:`VAO` instance
    """
    R = 1.0 / (rings - 1)
    S = 1.0 / (sectors - 1)

    vertices = [0] * (rings * sectors * 3)
    normals = [0] * (rings * sectors * 3)
    uvs = [0] * (rings * sectors * 2)

    v, n, t = 0, 0, 0
    for r in range(rings):
        for s in range(sectors):
            y = math.sin(-math.pi / 2 + math.pi * r * R)
            x = math.cos(2 * math.pi * s * S) * math.sin(math.pi * r * R)
            z = math.sin(2 * math.pi * s * S) * math.sin(math.pi * r * R)

            uvs[t] = s * S
            uvs[t + 1] = r * R

            vertices[v] = x * radius
            vertices[v + 1] = y * radius
            vertices[v + 2] = z * radius

            normals[n] = x
            normals[n + 1] = y
            normals[n + 2] = z

            t += 2
            v += 3
            n += 3

    indices = [0] * rings * sectors * 6
    i = 0
    for r in range(rings - 1):
        for s in range(sectors - 1):
            indices[i] = r * sectors + s
            indices[i + 1] = (r + 1) * sectors + (s + 1)
            indices[i + 2] = r * sectors + (s + 1)

            indices[i + 3] = r * sectors + s
            indices[i + 4] = (r + 1) * sectors + s
            indices[i + 5] = (r + 1) * sectors + (s + 1)
            i += 6

    vao = VAO(name or "sphere", mode=mlg.TRIANGLES)

    vbo_vertices = numpy.array(vertices, dtype=numpy.float32)
    vao.buffer(vbo_vertices, "3f", [attr_names.POSITION])

    if normals:
        vbo_normals = numpy.array(normals, dtype=numpy.float32)
        vao.buffer(vbo_normals, "3f", [attr_names.NORMAL])

    if uvs:
        vbo_uvs = numpy.array(uvs, dtype=numpy.float32)
        vao.buffer(vbo_uvs, "2f", [attr_names.TEXCOORD_0])

    vbo_elements = numpy.array(indices, dtype=numpy.uint32)
    vao.index_buffer(vbo_elements, index_element_size=4)

    return vao
Exemplo n.º 5
0
class VolumetricTetrahedralMesh(CameraWindow):
    """Volumetric Tetrahedral Mesh.

    The dataset was provided by:
    Mara Catalina Aguilera Canon at the Bournemouth University (UK).
    Area of research: Graph Neuro Networks, Finite Element Method

    An example rendering a volumetric mesh of the format:
    ``[[p1, p2, p3, p4], [p1, p2, p3, p4], ..]``
    were ```px``` represent a 3d point in a tetraherdon.
    A geometry shader calculates and emits the tetraherdons
    as triangles and calculate normals on the fly while rendering data.

    This helps us avoid doing this expensive operation
    in python and greatly reduces the memory requirement.

    Controls:
    - Camera: Mouse for rotation. AWSD + QE for translation
    - Press b to toggle blend mode on/off
    - Mouse wheel to increase or decrease the threshold for a tetra to be alive
    """
    gl_version = (4, 1)
    title = "Volumetric Tetrahedra lMesh"
    aspect_ratio = None
    resource_dir = (Path(__file__) / '../../resources').resolve()
    samples = 4

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        # Finetune camera
        self.wnd.mouse_exclusivity = True
        self.camera.projection.update(near=.01, far=100)
        self.camera.mouse_sensitivity = .5
        self.camera.velocity = 2.5
        self.camera.projection.update(fov=60)

        # Scene states
        self.with_blending = False
        self.line_color = (0.0, 0.0, 0.0)
        self.mesh_color = (0.0, 0.8, 0.0)
        self.threshold = 0.5

        # For rendering background
        self.quad_fs = geometry.quad_fs()

        # (172575,) | 57,525 vertices
        vertices = np.load(self.resource_dir /
                           'data/tetrahedral_mesh/mesh_nodes.npy')
        vertices = np.concatenate(vertices)
        # (259490, 4) (1037960,) indices
        indices = np.load(self.resource_dir /
                          'data/tetrahedral_mesh/element_nodes.npy')
        indices = np.concatenate(indices) - 1

        # Probability of a tetrahedron is still alive
        w, h = 8192, int(np.ceil(indices.shape[0] / 8192))
        self.alive_data = np.random.random_sample(w * h)
        self.alive_texture = self.ctx.texture((w, h), 1, dtype='f2')
        self.alive_texture.write(self.alive_data.astype('f2'))

        # Original geometry with indices
        self.geometry = VAO(name='geometry_indices')
        self.geometry.buffer(vertices, '3f', 'in_position')
        self.geometry.index_buffer(indices, index_element_size=4)

        self.prog_background = self.load_program(
            'programs/tetrahedral_mesh/bg.glsl')
        self.prog_gen_tetra = self.load_program(
            vertex_shader='programs/tetrahedral_mesh/gen_tetra_vert.glsl',
            geometry_shader='programs/tetrahedral_mesh/gen_tetra_geo.glsl',
            fragment_shader='programs/tetrahedral_mesh/gen_tetra_frag.glsl',
        )
        self.prog_gen_tetra_lines = self.load_program(
            vertex_shader='programs/tetrahedral_mesh/gen_tetra_vert.glsl',
            geometry_shader='programs/tetrahedral_mesh/gen_tetra_geo.glsl',
            fragment_shader='programs/tetrahedral_mesh/lines_frag.glsl',
        )

        # Query object for measuring the rendering call in OpenGL
        # It delivers the GPU time it took to process commands
        self.query = self.ctx.query(samples=True,
                                    any_samples=True,
                                    time=True,
                                    primitives=True)
        self.total_elapsed = 0

    def render(self, time, frametime):

        # Render background
        self.ctx.wireframe = False
        if not self.with_blending:
            self.ctx.enable_only(moderngl.NOTHING)
            self.quad_fs.render(self.prog_background)

        # Handle blend mode toggle
        if self.with_blending:
            self.ctx.enable_only(moderngl.BLEND)
            self.ctx.blend_func = moderngl.ONE, moderngl.ONE
        else:
            self.ctx.enable_only(moderngl.DEPTH_TEST | moderngl.CULL_FACE)

        # Render tetrahedral mesh
        translate = Matrix44.from_translation((0.0, 2.5, -15.0), dtype='f4')
        rotate = Matrix44.from_eulers((np.radians(180), 0, 0), dtype='f4')
        scale = Matrix44.from_scale((400, 400, 400), dtype='f4')
        mat = self.camera.matrix * translate * rotate * scale

        # All render calls inside this context are timed
        with self.query:
            self.alive_texture.use(location=0)
            self.prog_gen_tetra['alive_texture'].value = 0
            self.prog_gen_tetra['threshold'].value = self.threshold
            self.prog_gen_tetra['color'].value = self.mesh_color
            self.prog_gen_tetra['m_cam'].write(mat)
            self.prog_gen_tetra['m_proj'].write(self.camera.projection.matrix)
            self.geometry.render(self.prog_gen_tetra,
                                 mode=moderngl.LINES_ADJACENCY)

            # Render lines
            self.ctx.wireframe = True
            self.alive_texture.use(location=0)
            self.prog_gen_tetra_lines['alive_texture'].value = 0
            self.prog_gen_tetra_lines['threshold'].value = self.threshold
            self.prog_gen_tetra_lines['color'].value = self.line_color
            self.prog_gen_tetra_lines['m_cam'].write(mat)
            self.prog_gen_tetra_lines['m_proj'].write(
                self.camera.projection.matrix)
            self.geometry.render(self.prog_gen_tetra_lines,
                                 mode=moderngl.LINES_ADJACENCY)

        self.total_elapsed = self.query.elapsed

    def key_event(self, key, action, modifiers):
        super().key_event(key, action, modifiers)
        keys = self.wnd.keys

        if action == keys.ACTION_PRESS:
            if key == keys.B:
                self.with_blending = not self.with_blending
                print("With blending:", self.with_blending)
                if self.with_blending:
                    self.mesh_color = 0.01, 0.01, 0.01
                    self.line_color = 0.01, 0.01, 0.01
                else:
                    self.mesh_color = 0.0, 0.8, 0.0
                    self.line_color = 0.0, 0.0, 0.0

    def mouse_scroll_event(self, x_offset, y_offset):
        if y_offset > 0:
            self.threshold += 0.01
        else:
            self.threshold -= 0.01

        self.threshold = max(min(self.threshold, 1.0), 0.0)

    def close(self):
        # 1 s = 1000000000 ns
        # 1 s = 1000000 μs
        avg = self.total_elapsed / self.wnd.frames
        print("Average rendering time per frame: {} ns | {} μs".format(
            round(avg, 4),  # ns
            round(avg / 1000, 4),  # μs
        ))