def plane_xz(size=(10, 10), resolution=(10, 10)) -> VAO:
    """
    Generates a plane on the xz axis of a specific size and resolution.
    Normals and texture coordinates are also included.

    Args:
        size: (x, y) tuple
        resolution: (x, y) tuple

    Returns:
        A :py:class:`demosys.opengl.vao.VAO` instance
    """
    sx, sz = size
    rx, rz = resolution
    dx, dz = sx / rx, sz / rz  # step
    ox, oz = -sx / 2, -sz / 2  # start offset

    def gen_pos():
        for z in range(rz):
            for x in range(rx):
                yield ox + x * dx
                yield 0
                yield oz + z * dz

    def gen_uv():
        for z in range(rz):
            for x in range(rx):
                yield x / (rx - 1)
                yield 1 - z / (rz - 1)

    def gen_normal():
        for _ in range(rx * rz):
            yield 0.0
            yield 1.0
            yield 0.0

    def gen_index():
        for z in range(rz - 1):
            for x in range(rx - 1):
                # quad poly left
                yield z * rz + x + 1
                yield z * rz + x
                yield z * rz + x + rx
                # quad poly right
                yield z * rz + x + 1
                yield z * rz + x + rx
                yield z * rz + x + rx + 1

    pos_data = numpy.fromiter(gen_pos(), dtype=numpy.float32)
    uv_data = numpy.fromiter(gen_uv(), dtype=numpy.float32)
    normal_data = numpy.fromiter(gen_normal(), dtype=numpy.float32)
    index_data = numpy.fromiter(gen_index(), dtype=numpy.uint32)

    vao = VAO("plane_xz", mode=moderngl.TRIANGLES)

    vao.buffer(pos_data, '3f', ['in_position'])
    vao.buffer(uv_data, '2f', ['in_uv'])
    vao.buffer(normal_data, '3f', ['in_normal'])

    vao.index_buffer(index_data, index_element_size=4)

    return vao

def plane_xz ( size = ( 10 , 10 ) , resolution = ( 10 , 10 ) ) -> VAO : sx , sz = size rx , rz = resolution dx , dz = sx / rx , sz / rz ox , oz = - sx / 2 , - sz / 2 def gen_pos ( ) : for z in range ( rz ) : for x in range ( rx ) : yield ox + x * dx yield 0 yield oz + z * dz def gen_uv ( ) : for z in range ( rz ) : for x in range ( rx ) : yield x / ( rx - 1 ) yield 1 - z / ( rz - 1 ) def gen_normal ( ) : for _ in range ( rx * rz ) : yield 0.0 yield 1.0 yield 0.0 def gen_index ( ) : for z in range ( rz - 1 ) : for x in range ( rx - 1 ) : yield z * rz + x + 1 yield z * rz + x yield z * rz + x + rx yield z * rz + x + 1 yield z * rz + x + rx yield z * rz + x + rx + 1 pos_data = numpy . fromiter ( gen_pos ( ) , dtype = numpy . float32 ) uv_data = numpy . fromiter ( gen_uv ( ) , dtype = numpy . float32 ) normal_data = numpy . fromiter ( gen_normal ( ) , dtype = numpy . float32 ) index_data = numpy . fromiter ( gen_index ( ) , dtype = numpy . uint32 ) vao = VAO ( "plane_xz" , mode = moderngl . TRIANGLES ) vao . buffer ( pos_data , '3f' , [ 'in_position' ] ) vao . buffer ( uv_data , '2f' , [ 'in_uv' ] ) vao . buffer ( normal_data , '3f' , [ 'in_normal' ] ) vao . index_buffer ( index_data , index_element_size = 4 ) return vao

Generates a plane on the xz axis of a specific size and resolution . Normals and texture coordinates are also included .


def load(self):
        """
        Deferred loading of the scene

        :param scene: The scene object
        :param file: Resolved path if changed by finder
        """
        self.path = self.find_scene(self.meta.path)
        if not self.path:
            raise ValueError("Scene '{}' not found".format(self.meta.path))

        self.scene = Scene(self.path)

        # Load gltf json file
        if self.path.suffix == '.gltf':
            self.load_gltf()

        # Load binary gltf file
        if self.path.suffix == '.glb':
            self.load_glb()

        self.meta.check_version()
        self.meta.check_extensions(self.supported_extensions)
        self.load_images()
        self.load_samplers()
        self.load_textures()
        self.load_materials()
        self.load_meshes()
        self.load_nodes()

        self.scene.calc_scene_bbox()
        self.scene.prepare()

        return self.scene

def
load
(
self
)
:
self
.
path
=
self
.
find_scene
(
self
.
meta
.
path
)
if
not
self
.
path
:
raise
ValueError
(
"Scene '{}' not found"
.
format
(
self
.
meta
.
path
)
)
self
.
scene
=
Scene
(
self
.
path
)
if
self
.
path
.
suffix
==
'.gltf'
:
self
.
load_gltf
(
)
if
self
.
path
.
suffix
==
'.glb'
:
self
.
load_glb
(
)
self
.
meta
.
check_version
(
)
self
.
meta
.
check_extensions
(
self
.
supported_extensions
)
self
.
load_images
(
)
self
.
load_samplers
(
)
self
.
load_textures
(
)
self
.
load_materials
(
)
self
.
load_meshes
(
)
self
.
load_nodes
(
)
self
.
scene
.
calc_scene_bbox
(
)
self
.
scene
.
prepare
(
)
return
self
.
scene


def load_gltf(self):
        """Loads a gltf json file"""
        with open(self.path) as fd:
            self.meta = GLTFMeta(self.path, json.load(fd))

def load_gltf ( self ) : with open ( self . path ) as fd : self . meta = GLTFMeta ( self . path , json . load ( fd ) )


def load_glb(self):
        """Loads a binary gltf file"""
        with open(self.path, 'rb') as fd:
            # Check header
            magic = fd.read(4)
            if magic != GLTF_MAGIC_HEADER:
                raise ValueError("{} has incorrect header {} != {}".format(self.path, magic, GLTF_MAGIC_HEADER))

            version = struct.unpack('<I', fd.read(4))[0]
            if version != 2:
                raise ValueError("{} has unsupported version {}".format(self.path, version))

            # Total file size including headers
            _ = struct.unpack('<I', fd.read(4))[0]  # noqa

            # Chunk 0 - json
            chunk_0_length = struct.unpack('<I', fd.read(4))[0]
            chunk_0_type = fd.read(4)
            if chunk_0_type != b'JSON':
                raise ValueError("Expected JSON chunk, not {} in file {}".format(chunk_0_type, self.path))

            json_meta = fd.read(chunk_0_length).decode()

            # chunk 1 - binary buffer
            chunk_1_length = struct.unpack('<I', fd.read(4))[0]
            chunk_1_type = fd.read(4)
            if chunk_1_type != b'BIN\x00':
                raise ValueError("Expected BIN chunk, not {} in file {}".format(chunk_1_type, self.path))

            self.meta = GLTFMeta(self.path, json.loads(json_meta), binary_buffer=fd.read(chunk_1_length))


def buffers_exist(self):
        """Checks if the bin files referenced exist"""
        for buff in self.buffers:
            if not buff.is_separate_file:
                continue

            path = self.path.parent / buff.uri
            if not os.path.exists(path):
                raise FileNotFoundError("Buffer {} referenced in {} not found".format(path, self.path))

def
buffers_exist
(
self
)
:
for
buff
in
self
.
buffers
:
if
not
buff
.
is_separate_file
:
continue
path
=
self
.
path
.
parent
/
buff
.
uri
if
not
os
.
path
.
exists
(
path
)
:
raise
FileNotFoundError
(
"Buffer {} referenced in {} not found"
.
format
(
path
,
self
.
path
)
)


def prepare_attrib_mapping(self, primitive):
        """Pre-parse buffer mappings for each VBO to detect interleaved data for a primitive"""
        buffer_info = []
        for name, accessor in primitive.attributes.items():
            info = VBOInfo(*accessor.info())
            info.attributes.append((name, info.components))

            if buffer_info and buffer_info[-1].buffer_view == info.buffer_view:
                if buffer_info[-1].interleaves(info):
                    buffer_info[-1].merge(info)
                    continue

            buffer_info.append(info)

        return buffer_info

def prepare_attrib_mapping ( self , primitive ) : buffer_info = [ ] for name , accessor in primitive . attributes . items ( ) : info = VBOInfo ( * accessor . info ( ) ) info . attributes . append ( ( name , info . components ) ) if buffer_info and buffer_info [ - 1 ] . buffer_view == info . buffer_view : if buffer_info [ - 1 ] . interleaves ( info ) : buffer_info [ - 1 ] . merge ( info ) continue buffer_info . append ( info ) return buffer_info

Pre - parse buffer mappings for each VBO to detect interleaved data for a primitive


def get_bbox(self, primitive):
        """Get the bounding box for the mesh"""
        accessor = primitive.attributes.get('POSITION')
        return accessor.min, accessor.max

def get_bbox ( self , primitive ) : accessor = primitive . attributes . get ( 'POSITION' ) return accessor . min , accessor . max


def interleaves(self, info):
        """Does the buffer interleave with this one?"""
        return info.byte_offset == self.component_type.size * self.components

def interleaves ( self , info ) : return info . byte_offset == self . component_type . size * self . components


def create(self):
        """Create the VBO"""
        dtype = NP_COMPONENT_DTYPE[self.component_type.value]
        data = numpy.frombuffer(
            self.buffer.read(byte_length=self.byte_length, byte_offset=self.byte_offset),
            count=self.count * self.components,
            dtype=dtype,
        )
        return dtype, data

def create ( self ) : dtype = NP_COMPONENT_DTYPE [ self . component_type . value ] data = numpy . frombuffer ( self . buffer . read ( byte_length = self . byte_length , byte_offset = self . byte_offset ) , count = self . count * self . components , dtype = dtype , ) return dtype , data


def set_position(self, x, y, z):
        """
        Set the 3D position of the camera

        :param x: float
        :param y: float
        :param z: float
        """
        self.position = Vector3([x, y, z])

def set_position ( self , x , y , z ) : self . position = Vector3 ( [ x , y , z ] )


def _update_yaw_and_pitch(self):
        """
        Updates the camera vectors based on the current yaw and pitch
        """
        front = Vector3([0.0, 0.0, 0.0])
        front.x = cos(radians(self.yaw)) * cos(radians(self.pitch))
        front.y = sin(radians(self.pitch))
        front.z = sin(radians(self.yaw)) * cos(radians(self.pitch))

        self.dir = vector.normalise(front)
        self.right = vector.normalise(vector3.cross(self.dir, self._up))
        self.up = vector.normalise(vector3.cross(self.right, self.dir))

def _update_yaw_and_pitch ( self ) : front = Vector3 ( [ 0.0 , 0.0 , 0.0 ] ) front . x = cos ( radians ( self . yaw ) ) * cos ( radians ( self . pitch ) ) front . y = sin ( radians ( self . pitch ) ) front . z = sin ( radians ( self . yaw ) ) * cos ( radians ( self . pitch ) ) self . dir = vector . normalise ( front ) self . right = vector . normalise ( vector3 . cross ( self . dir , self . _up ) ) self . up = vector . normalise ( vector3 . cross ( self . right , self . dir ) )


def look_at(self, vec=None, pos=None):
        """
        Look at a specific point

        :param vec: Vector3 position
        :param pos: python list [x, y, x]
        :return: Camera matrix
        """
        if pos is None:
            vec = Vector3(pos)

        if vec is None:
            raise ValueError("vector or pos must be set")

        return self._gl_look_at(self.position, vec, self._up)

def
look_at
(
self
,
vec
=
,
pos
=
)
:
if
pos
is
:
vec
=
Vector3
(
pos
)
if
vec
is
:
raise
ValueError
(
"vector or pos must be set"
)
return
self
.
_gl_look_at
(
self
.
position
,
vec
,
self
.
_up
)


def _gl_look_at(self, pos, target, up):
        """
        The standard lookAt method

        :param pos: current position
        :param target: target position to look at
        :param up: direction up
        """
        z = vector.normalise(pos - target)
        x = vector.normalise(vector3.cross(vector.normalise(up), z))
        y = vector3.cross(z, x)

        translate = matrix44.create_identity()
        translate[3][0] = -pos.x
        translate[3][1] = -pos.y
        translate[3][2] = -pos.z

        rotate = matrix44.create_identity()
        rotate[0][0] = x[0]  # -- X
        rotate[1][0] = x[1]
        rotate[2][0] = x[2]
        rotate[0][1] = y[0]  # -- Y
        rotate[1][1] = y[1]
        rotate[2][1] = y[2]
        rotate[0][2] = z[0]  # -- Z
        rotate[1][2] = z[1]
        rotate[2][2] = z[2]

        return matrix44.multiply(translate, rotate)

def _gl_look_at ( self , pos , target , up ) : z = vector . normalise ( pos - target ) x = vector . normalise ( vector3 . cross ( vector . normalise ( up ) , z ) ) y = vector3 . cross ( z , x ) translate = matrix44 . create_identity ( ) translate [ 3 ] [ 0 ] = - pos . x translate [ 3 ] [ 1 ] = - pos . y translate [ 3 ] [ 2 ] = - pos . z rotate = matrix44 . create_identity ( ) rotate [ 0 ] [ 0 ] = x [ 0 ] rotate [ 1 ] [ 0 ] = x [ 1 ] rotate [ 2 ] [ 0 ] = x [ 2 ] rotate [ 0 ] [ 1 ] = y [ 0 ] rotate [ 1 ] [ 1 ] = y [ 1 ] rotate [ 2 ] [ 1 ] = y [ 2 ] rotate [ 0 ] [ 2 ] = z [ 0 ] rotate [ 1 ] [ 2 ] = z [ 1 ] rotate [ 2 ] [ 2 ] = z [ 2 ] return matrix44 . multiply ( translate , rotate )


def move_state(self, direction, activate):
        """
        Set the camera position move state

        :param direction: What direction to update
        :param activate: Start or stop moving in the direction
        """
        if direction == RIGHT:
            self._xdir = POSITIVE if activate else STILL
        elif direction == LEFT:
            self._xdir = NEGATIVE if activate else STILL
        elif direction == FORWARD:
            self._zdir = NEGATIVE if activate else STILL
        elif direction == BACKWARD:
            self._zdir = POSITIVE if activate else STILL
        elif direction == UP:
            self._ydir = POSITIVE if activate else STILL
        elif direction == DOWN:
            self._ydir = NEGATIVE if activate else STILL

def move_state ( self , direction , activate ) : if direction == RIGHT : self . _xdir = POSITIVE if activate else STILL elif direction == LEFT : self . _xdir = NEGATIVE if activate else STILL elif direction == FORWARD : self . _zdir = NEGATIVE if activate else STILL elif direction == BACKWARD : self . _zdir = POSITIVE if activate else STILL elif direction == UP : self . _ydir = POSITIVE if activate else STILL elif direction == DOWN : self . _ydir = NEGATIVE if activate else STILL


def rot_state(self, x, y):
        """
        Set the rotation state of the camera

        :param x: viewport x pos
        :param y: viewport y pos
        """
        if self.last_x is None:
            self.last_x = x
        if self.last_y is None:
            self.last_y = y

        x_offset = self.last_x - x
        y_offset = self.last_y - y

        self.last_x = x
        self.last_y = y

        x_offset *= self.mouse_sensitivity
        y_offset *= self.mouse_sensitivity

        self.yaw -= x_offset
        self.pitch += y_offset

        if self.pitch > 85.0:
            self.pitch = 85.0
        if self.pitch < -85.0:
            self.pitch = -85.0

        self._update_yaw_and_pitch()

def rot_state ( self , x , y ) : if self . last_x is None : self . last_x = x if self . last_y is None : self . last_y = y x_offset = self . last_x - x y_offset = self . last_y - y self . last_x = x self . last_y = y x_offset *= self . mouse_sensitivity y_offset *= self . mouse_sensitivity self . yaw -= x_offset self . pitch += y_offset if self . pitch > 85.0 : self . pitch = 85.0 if self . pitch < - 85.0 : self . pitch = - 85.0 self . _update_yaw_and_pitch ( )


def _translate_string(self, data, length):
        """Translate string into character texture positions"""
        for index, char in enumerate(data):
            if index == length:
                break

            yield self._meta.characters - 1 - self._ct[char]

def _translate_string ( self , data , length ) : for index , char in enumerate ( data ) : if index == length : break yield self . _meta . characters - 1 - self . _ct [ char ]


def init(window=None, project=None, timeline=None):
    """
    Initialize, load and run

    :param manager: The effect manager to use
    """
    from demosys.effects.registry import Effect
    from demosys.scene import camera

    window.timeline = timeline

    # Inject attributes into the base Effect class
    setattr(Effect, '_window', window)
    setattr(Effect, '_ctx', window.ctx)
    setattr(Effect, '_project', project)

    # Set up the default system camera
    window.sys_camera = camera.SystemCamera(aspect=window.aspect_ratio, fov=60.0, near=1, far=1000)
    setattr(Effect, '_sys_camera', window.sys_camera)

    print("Loading started at", time.time())
    project.load()

    # Initialize timer
    timer_cls = import_string(settings.TIMER)
    window.timer = timer_cls()
    window.timer.start()

def
init
(
window
=
,
project
=
,
timeline
=
)
:
from
demosys
.
effects
.
registry
import
Effect
from
demosys
.
scene
import
camera
window
.
timeline
=
timeline
setattr
(
Effect
,
'_window'
,
window
)
setattr
(
Effect
,
'_ctx'
,
window
.
ctx
)
setattr
(
Effect
,
'_project'
,
project
)
window
.
sys_camera
=
camera
.
SystemCamera
(
aspect
=
window
.
aspect_ratio
,
fov
=
60.0
,
near
=
1
,
far
=
1000
)
setattr
(
Effect
,
'_sys_camera'
,
window
.
sys_camera
)
print
(
"Loading started at"
,
time
.
time
(
)
)
project
.
load
(
)
timer_cls
=
import_string
(
settings
.
TIMER
)
window
.
timer
=
timer_cls
(
)
window
.
timer
.
start
(
)


def draw(self, projection_matrix=None, camera_matrix=None, time=0):
        """
        Draw all the nodes in the scene

        :param projection_matrix: projection matrix (bytes)
        :param camera_matrix: camera_matrix (bytes)
        :param time: The current time
        """
        projection_matrix = projection_matrix.astype('f4').tobytes()
        camera_matrix = camera_matrix.astype('f4').tobytes()

        for node in self.root_nodes:
            node.draw(
                projection_matrix=projection_matrix,
                camera_matrix=camera_matrix,
                time=time,
            )

        self.ctx.clear_samplers(0, 4)

def draw ( self , projection_matrix = None , camera_matrix = None , time = 0 ) : projection_matrix = projection_matrix . astype ( 'f4' ) . tobytes ( ) camera_matrix = camera_matrix . astype ( 'f4' ) . tobytes ( ) for node in self . root_nodes : node . draw ( projection_matrix = projection_matrix , camera_matrix = camera_matrix , time = time , ) self . ctx . clear_samplers ( 0 , 4 )


def draw_bbox(self, projection_matrix=None, camera_matrix=None, all=True):
        """Draw scene and mesh bounding boxes"""
        projection_matrix = projection_matrix.astype('f4').tobytes()
        camera_matrix = camera_matrix.astype('f4').tobytes()

        # Scene bounding box
        self.bbox_program["m_proj"].write(projection_matrix)
        self.bbox_program["m_view"].write(self._view_matrix.astype('f4').tobytes())
        self.bbox_program["m_cam"].write(camera_matrix)
        self.bbox_program["bb_min"].write(self.bbox_min.astype('f4').tobytes())
        self.bbox_program["bb_max"].write(self.bbox_max.astype('f4').tobytes())
        self.bbox_program["color"].value = (1.0, 0.0, 0.0)
        self.bbox_vao.render(self.bbox_program)

        if not all:
            return

        # Draw bounding box for children
        for node in self.root_nodes:
            node.draw_bbox(projection_matrix, camera_matrix, self.bbox_program, self.bbox_vao)

def draw_bbox ( self , projection_matrix = None , camera_matrix = None , all = True ) : projection_matrix = projection_matrix . astype ( 'f4' ) . tobytes ( ) camera_matrix = camera_matrix . astype ( 'f4' ) . tobytes ( ) self . bbox_program [ "m_proj" ] . write ( projection_matrix ) self . bbox_program [ "m_view" ] . write ( self . _view_matrix . astype ( 'f4' ) . tobytes ( ) ) self . bbox_program [ "m_cam" ] . write ( camera_matrix ) self . bbox_program [ "bb_min" ] . write ( self . bbox_min . astype ( 'f4' ) . tobytes ( ) ) self . bbox_program [ "bb_max" ] . write ( self . bbox_max . astype ( 'f4' ) . tobytes ( ) ) self . bbox_program [ "color" ] . value = ( 1.0 , 0.0 , 0.0 ) self . bbox_vao . render ( self . bbox_program ) if not all : return for node in self . root_nodes : node . draw_bbox ( projection_matrix , camera_matrix , self . bbox_program , self . bbox_vao )


def apply_mesh_programs(self, mesh_programs=None):
        """Applies mesh programs to meshes"""
        if not mesh_programs:
            mesh_programs = [ColorProgram(), TextureProgram(), FallbackProgram()]

        for mesh in self.meshes:
            for mp in mesh_programs:
                instance = mp.apply(mesh)
                if instance is not None:
                    if isinstance(instance, MeshProgram):
                        mesh.mesh_program = mp
                        break
                    else:
                        raise ValueError("apply() must return a MeshProgram instance, not {}".format(type(instance)))

            if not mesh.mesh_program:
                print("WARING: No mesh program applied to '{}'".format(mesh.name))


def calc_scene_bbox(self):
        """Calculate scene bbox"""
        bbox_min, bbox_max = None, None
        for node in self.root_nodes:
            bbox_min, bbox_max = node.calc_global_bbox(
                matrix44.create_identity(),
                bbox_min,
                bbox_max
            )

        self.bbox_min = bbox_min
        self.bbox_max = bbox_max

        self.diagonal_size = vector3.length(self.bbox_max - self.bbox_min)

def calc_scene_bbox ( self ) : bbox_min , bbox_max = None , None for node in self . root_nodes : bbox_min , bbox_max = node . calc_global_bbox ( matrix44 . create_identity ( ) , bbox_min , bbox_max ) self . bbox_min = bbox_min self . bbox_max = bbox_max self . diagonal_size = vector3 . length ( self . bbox_max - self . bbox_min )


def points_random_3d(count, range_x=(-10.0, 10.0), range_y=(-10.0, 10.0), range_z=(-10.0, 10.0), seed=None) -> VAO:
    """
    Generates random positions inside a confied box.

    Args:
        count (int): Number of points to generate

    Keyword Args:
        range_x (tuple): min-max range for x axis: Example (-10.0. 10.0)
        range_y (tuple): min-max range for y axis: Example (-10.0. 10.0)
        range_z (tuple): min-max range for z axis: Example (-10.0. 10.0)
        seed (int): The random seed

    Returns:
        A :py:class:`demosys.opengl.vao.VAO` instance
    """
    random.seed(seed)

    def gen():
        for _ in range(count):
            yield random.uniform(*range_x)
            yield random.uniform(*range_y)
            yield random.uniform(*range_z)

    data = numpy.fromiter(gen(), count=count * 3, dtype=numpy.float32)

    vao = VAO("geometry:points_random_3d", mode=moderngl.POINTS)
    vao.buffer(data, '3f', ['in_position'])

    return vao

def points_random_3d ( count , range_x = ( - 10.0 , 10.0 ) , range_y = ( - 10.0 , 10.0 ) , range_z = ( - 10.0 , 10.0 ) , seed = None ) -> VAO : random . seed ( seed ) def gen ( ) : for _ in range ( count ) : yield random . uniform ( * range_x ) yield random . uniform ( * range_y ) yield random . uniform ( * range_z ) data = numpy . fromiter ( gen ( ) , count = count * 3 , dtype = numpy . float32 ) vao = VAO ( "geometry:points_random_3d" , mode = moderngl . POINTS ) vao . buffer ( data , '3f' , [ 'in_position' ] ) return vao

Generates random positions inside a confied box .


def start(self):
        """Play the music"""
        if self.initialized:
            mixer.music.unpause()
        else:
            mixer.music.play()
            # FIXME: Calling play twice to ensure the music is actually playing
            mixer.music.play()
            self.initialized = True
        self.paused = False

def start ( self ) : if self . initialized : mixer . music . unpause ( ) else : mixer . music . play ( ) mixer . music . play ( ) self . initialized = True self . paused = False


def get_time(self) -> float:
        """
        Get the current position in the music in seconds
        """
        if self.paused:
            return self.pause_time

        return mixer.music.get_pos() / 1000.0

def get_time ( self ) -> float : if self . paused : return self . pause_time return mixer . music . get_pos ( ) / 1000.0


def set_time(self, value: float):
        """
        Set the current time in the music in seconds causing the player
        to seek to this location in the file.
        """
        if value < 0:
            value = 0

        # mixer.music.play(start=value)
        mixer.music.set_pos(value)

def set_time ( self , value : float ) : if value < 0 : value = 0 mixer . music . set_pos ( value )

Set the current time in the music in seconds causing the player to seek to this location in the file .


def draw_buffers(self, near, far):
        """
        Draw framebuffers for debug purposes.
        We need to supply near and far plane so the depth buffer can be linearized when visualizing.

        :param near: Projection near value
        :param far: Projection far value
        """
        self.ctx.disable(moderngl.DEPTH_TEST)

        helper.draw(self.gbuffer.color_attachments[0], pos=(0.0, 0.0), scale=(0.25, 0.25))
        helper.draw(self.gbuffer.color_attachments[1], pos=(0.5, 0.0), scale=(0.25, 0.25))
        helper.draw_depth(self.gbuffer.depth_attachment, near, far, pos=(1.0, 0.0), scale=(0.25, 0.25))
        helper.draw(self.lightbuffer.color_attachments[0], pos=(1.5, 0.0), scale=(0.25, 0.25))

def draw_buffers ( self , near , far ) : self . ctx . disable ( moderngl . DEPTH_TEST ) helper . draw ( self . gbuffer . color_attachments [ 0 ] , pos = ( 0.0 , 0.0 ) , scale = ( 0.25 , 0.25 ) ) helper . draw ( self . gbuffer . color_attachments [ 1 ] , pos = ( 0.5 , 0.0 ) , scale = ( 0.25 , 0.25 ) ) helper . draw_depth ( self . gbuffer . depth_attachment , near , far , pos = ( 1.0 , 0.0 ) , scale = ( 0.25 , 0.25 ) ) helper . draw ( self . lightbuffer . color_attachments [ 0 ] , pos = ( 1.5 , 0.0 ) , scale = ( 0.25 , 0.25 ) )

Draw framebuffers for debug purposes . We need to supply near and far plane so the depth buffer can be linearized when visualizing .


def add_point_light(self, position, radius):
        """Add point light"""
        self.point_lights.append(PointLight(position, radius))

def add_point_light ( self , position , radius ) : self . point_lights . append ( PointLight ( position , radius ) )


def render_lights(self, camera_matrix, projection):
        """Render light volumes"""
        # Draw light volumes from the inside
        self.ctx.front_face = 'cw'
        self.ctx.blend_func = moderngl.ONE, moderngl.ONE

        helper._depth_sampler.use(location=1)
        with self.lightbuffer_scope:
            for light in self.point_lights:
                # Calc light properties
                light_size = light.radius
                m_light = matrix44.multiply(light.matrix, camera_matrix)
                # Draw the light volume
                self.point_light_shader["m_proj"].write(projection.tobytes())
                self.point_light_shader["m_light"].write(m_light.astype('f4').tobytes())
                self.gbuffer.color_attachments[1].use(location=0)
                self.point_light_shader["g_normal"].value = 0
                self.gbuffer.depth_attachment.use(location=1)
                self.point_light_shader["g_depth"].value = 1
                self.point_light_shader["screensize"].value = (self.width, self.height)
                self.point_light_shader["proj_const"].value = projection.projection_constants
                self.point_light_shader["radius"].value = light_size
                self.unit_cube.render(self.point_light_shader)

        helper._depth_sampler.clear(location=1)

def render_lights ( self , camera_matrix , projection ) : self . ctx . front_face = 'cw' self . ctx . blend_func = moderngl . ONE , moderngl . ONE helper . _depth_sampler . use ( location = 1 ) with self . lightbuffer_scope : for light in self . point_lights : light_size = light . radius m_light = matrix44 . multiply ( light . matrix , camera_matrix ) self . point_light_shader [ "m_proj" ] . write ( projection . tobytes ( ) ) self . point_light_shader [ "m_light" ] . write ( m_light . astype ( 'f4' ) . tobytes ( ) ) self . gbuffer . color_attachments [ 1 ] . use ( location = 0 ) self . point_light_shader [ "g_normal" ] . value = 0 self . gbuffer . depth_attachment . use ( location = 1 ) self . point_light_shader [ "g_depth" ] . value = 1 self . point_light_shader [ "screensize" ] . value = ( self . width , self . height ) self . point_light_shader [ "proj_const" ] . value = projection . projection_constants self . point_light_shader [ "radius" ] . value = light_size self . unit_cube . render ( self . point_light_shader ) helper . _depth_sampler . clear ( location = 1 )


def render_lights_debug(self, camera_matrix, projection):
        """Render outlines of light volumes"""
        self.ctx.enable(moderngl.BLEND)
        self.ctx.blend_func = moderngl.SRC_ALPHA, moderngl.ONE_MINUS_SRC_ALPHA

        for light in self.point_lights:
            m_mv = matrix44.multiply(light.matrix, camera_matrix)
            light_size = light.radius
            self.debug_shader["m_proj"].write(projection.tobytes())
            self.debug_shader["m_mv"].write(m_mv.astype('f4').tobytes())
            self.debug_shader["size"].value = light_size
            self.unit_cube.render(self.debug_shader, mode=moderngl.LINE_STRIP)

        self.ctx.disable(moderngl.BLEND)

def render_lights_debug ( self , camera_matrix , projection ) : self . ctx . enable ( moderngl . BLEND ) self . ctx . blend_func = moderngl . SRC_ALPHA , moderngl . ONE_MINUS_SRC_ALPHA for light in self . point_lights : m_mv = matrix44 . multiply ( light . matrix , camera_matrix ) light_size = light . radius self . debug_shader [ "m_proj" ] . write ( projection . tobytes ( ) ) self . debug_shader [ "m_mv" ] . write ( m_mv . astype ( 'f4' ) . tobytes ( ) ) self . debug_shader [ "size" ] . value = light_size self . unit_cube . render ( self . debug_shader , mode = moderngl . LINE_STRIP ) self . ctx . disable ( moderngl . BLEND )


def combine(self):
        """Combine diffuse and light buffer"""
        self.gbuffer.color_attachments[0].use(location=0)
        self.combine_shader["diffuse_buffer"].value = 0
        self.lightbuffer.color_attachments[0].use(location=1)
        self.combine_shader["light_buffer"].value = 1
        self.quad.render(self.combine_shader)

def combine ( self ) : self . gbuffer . color_attachments [ 0 ] . use ( location = 0 ) self . combine_shader [ "diffuse_buffer" ] . value = 0 self . lightbuffer . color_attachments [ 0 ] . use ( location = 1 ) self . combine_shader [ "light_buffer" ] . value = 1 self . quad . render ( self . combine_shader )


def load_shader(self, shader_type: str, path: str):
        """Load a single shader"""
        if path:
            resolved_path = self.find_program(path)
            if not resolved_path:
                raise ValueError("Cannot find {} shader '{}'".format(shader_type, path))

            print("Loading:", path)

            with open(resolved_path, 'r') as fd:
                return fd.read()

def
load_shader
(
self
,
shader_type
:
str
,
path
:
str
)
:
if
path
:
resolved_path
=
self
.
find_program
(
path
)
if
not
resolved_path
:
raise
ValueError
(
"Cannot find {} shader '{}'"
.
format
(
shader_type
,
path
)
)
print
(
"Loading:"
,
path
)
with
open
(
resolved_path
,
'r'
)
as
fd
:
return
fd
.
read
(
)


def load(self):
        """Load a texture array"""
        self._open_image()

        width, height, depth = self.image.size[0], self.image.size[1] // self.layers, self.layers
        components, data = image_data(self.image)

        texture = self.ctx.texture_array(
            (width, height, depth),
            components,
            data,
        )
        texture.extra = {'meta': self.meta}

        if self.meta.mipmap:
            texture.build_mipmaps()

        self._close_image()

        return texture

def load ( self ) : self . _open_image ( ) width , height , depth = self . image . size [ 0 ] , self . image . size [ 1 ] // self . layers , self . layers components , data = image_data ( self . image ) texture = self . ctx . texture_array ( ( width , height , depth ) , components , data , ) texture . extra = { 'meta' : self . meta } if self . meta . mipmap : texture . build_mipmaps ( ) self . _close_image ( ) return texture


def draw(self, projection_matrix=None, view_matrix=None, camera_matrix=None, time=0):
        """
        Draw the mesh using the assigned mesh program

        :param projection_matrix: projection_matrix (bytes)
        :param view_matrix: view_matrix (bytes)
        :param camera_matrix: camera_matrix (bytes)
        """
        if self.mesh_program:
            self.mesh_program.draw(
                self,
                projection_matrix=projection_matrix,
                view_matrix=view_matrix,
                camera_matrix=camera_matrix,
                time=time
            )

def draw ( self , projection_matrix = None , view_matrix = None , camera_matrix = None , time = 0 ) : if self . mesh_program : self . mesh_program . draw ( self , projection_matrix = projection_matrix , view_matrix = view_matrix , camera_matrix = camera_matrix , time = time )


def set_time(self, value: float):
        """
        Set the current time jumping in the timeline.

        Args:
            value (float): The new time
        """
        if value < 0:
            value = 0

        self.controller.row = self.rps * value

def set_time ( self , value : float ) : if value < 0 : value = 0 self . controller . row = self . rps * value

Set the current time jumping in the timeline .


def draw(self, time: float, frametime: float, target: moderngl.Framebuffer):
        """
        Draw function called by the system every frame when the effect is active.
        This method raises ``NotImplementedError`` unless implemented.

        Args:
            time (float): The current time in seconds.
            frametime (float): The time the previous frame used to render in seconds.
            target (``moderngl.Framebuffer``): The target FBO for the effect.
        """
        raise NotImplementedError("draw() is not implemented")

def
draw
(
self
,
time
:
float
,
frametime
:
float
,
target
:
moderngl
.
Framebuffer
)
:
raise
NotImplementedError
(
"draw() is not implemented"
)

Draw function called by the system every frame when the effect is active . This method raises NotImplementedError unless implemented .


def get_program(self, label: str) -> moderngl.Program:
        """
        Get a program by its label

        Args:
            label (str): The label for the program

        Returns: py:class:`moderngl.Program` instance
        """
        return self._project.get_program(label)

def get_program ( self , label : str ) -> moderngl . Program : return self . _project . get_program ( label )


def get_texture(self, label: str) -> Union[moderngl.Texture, moderngl.TextureArray,
                                               moderngl.Texture3D, moderngl.TextureCube]:
        """
        Get a texture by its label

        Args:
            label (str): The Label for the texture

        Returns:
            The py:class:`moderngl.Texture` instance
        """
        return self._project.get_texture(label)

def get_texture ( self , label : str ) -> Union [ moderngl . Texture , moderngl . TextureArray , moderngl . Texture3D , moderngl . TextureCube ] : return self . _project . get_texture ( label )


def get_effect_class(self, effect_name: str, package_name: str = None) -> Type['Effect']:
        """
        Get an effect class by the class name

        Args:
            effect_name (str): Name of the effect class

        Keyword Args:
            package_name (str): The package the effect belongs to. This is optional and only
                                needed when effect class names are not unique.

        Returns:
            :py:class:`Effect` class
        """
        return self._project.get_effect_class(effect_name, package_name=package_name)

def get_effect_class ( self , effect_name : str , package_name : str = None ) -> Type [ 'Effect' ] : return self . _project . get_effect_class ( effect_name , package_name = package_name )


def create_projection(self, fov: float = 75.0, near: float = 1.0, far: float = 100.0, aspect_ratio: float = None):
        """
        Create a projection matrix with the following parameters.
        When ``aspect_ratio`` is not provided the configured aspect
        ratio for the window will be used.

        Args:
            fov (float): Field of view (float)
            near (float): Camera near value
            far (float): Camrea far value

        Keyword Args:
            aspect_ratio (float): Aspect ratio of the viewport

        Returns:
            The projection matrix as a float32 :py:class:`numpy.array`
        """
        return matrix44.create_perspective_projection_matrix(
            fov,
            aspect_ratio or self.window.aspect_ratio,
            near,
            far,
            dtype='f4',
        )

def create_projection ( self , fov : float = 75.0 , near : float = 1.0 , far : float = 100.0 , aspect_ratio : float = None ) : return matrix44 . create_perspective_projection_matrix ( fov , aspect_ratio or self . window . aspect_ratio , near , far , dtype = 'f4' , )

Create a projection matrix with the following parameters . When aspect_ratio is not provided the configured aspect ratio for the window will be used .


def create_transformation(self, rotation=None, translation=None):
        """
        Creates a transformation matrix woth rotations and translation.

        Args:
            rotation: 3 component vector as a list, tuple, or :py:class:`pyrr.Vector3`
            translation: 3 component vector as a list, tuple, or :py:class:`pyrr.Vector3`

        Returns:
            A 4x4 matrix as a :py:class:`numpy.array`
        """
        mat = None
        if rotation is not None:
            mat = Matrix44.from_eulers(Vector3(rotation))

        if translation is not None:
            trans = matrix44.create_from_translation(Vector3(translation))
            if mat is None:
                mat = trans
            else:
                mat = matrix44.multiply(mat, trans)

        return mat

def create_transformation ( self , rotation = None , translation = None ) : mat = None if rotation is not None : mat = Matrix44 . from_eulers ( Vector3 ( rotation ) ) if translation is not None : trans = matrix44 . create_from_translation ( Vector3 ( translation ) ) if mat is None : mat = trans else : mat = matrix44 . multiply ( mat , trans ) return mat

Creates a transformation matrix woth rotations and translation .


def create_normal_matrix(self, modelview):
        """
        Creates a normal matrix from modelview matrix

        Args:
            modelview: The modelview matrix

        Returns:
            A 3x3 Normal matrix as a :py:class:`numpy.array`
        """
        normal_m = Matrix33.from_matrix44(modelview)
        normal_m = normal_m.inverse
        normal_m = normal_m.transpose()
        return normal_m

def create_normal_matrix ( self , modelview ) : normal_m = Matrix33 . from_matrix44 ( modelview ) normal_m = normal_m . inverse normal_m = normal_m . transpose ( ) return normal_m


def available_templates(value):
    """Scan for available templates in effect_templates"""
    templates = list_templates()

    if value not in templates:
        raise ArgumentTypeError("Effect template '{}' does not exist.\n Available templates: {} ".format(
            value, ", ".join(templates)))

    return value


def root_path():
    """Get the absolute path to the root of the demosys package"""
    module_dir = os.path.dirname(globals()['__file__'])
    return os.path.dirname(os.path.dirname(module_dir))

def root_path ( ) : module_dir = os . path . dirname ( globals ( ) [ '__file__' ] ) return os . path . dirname ( os . path . dirname ( module_dir ) )


def load(self):
        """Load a file in text mode"""
        self.meta.resolved_path = self.find_data(self.meta.path)

        if not self.meta.resolved_path:
            raise ImproperlyConfigured("Data file '{}' not found".format(self.meta.path))

        print("Loading:", self.meta.path)

        with open(self.meta.resolved_path, 'r') as fd:
            return fd.read()

def
load
(
self
)
:
self
.
meta
.
resolved_path
=
self
.
find_data
(
self
.
meta
.
path
)
if
not
self
.
meta
.
resolved_path
:
raise
ImproperlyConfigured
(
"Data file '{}' not found"
.
format
(
self
.
meta
.
path
)
)
print
(
"Loading:"
,
self
.
meta
.
path
)
with
open
(
self
.
meta
.
resolved_path
,
'r'
)
as
fd
:
return
fd
.
read
(
)


def get_finder(import_path):
    """
    Get a finder class from an import path.
    Raises ``demosys.core.exceptions.ImproperlyConfigured`` if the finder is not found.
    This function uses an lru cache.

    :param import_path: string representing an import path
    :return: An instance of the finder
    """
    Finder = import_string(import_path)
    if not issubclass(Finder, BaseFileSystemFinder):
        raise ImproperlyConfigured('Finder {} is not a subclass of core.finders.FileSystemFinder'.format(import_path))
    return Finder()

Get a finder class from an import path . Raises demosys . core . exceptions . ImproperlyConfigured if the finder is not found . This function uses an lru cache .


def find(self, path: Path):
        """
        Find a file in the path. The file may exist in multiple
        paths. The last found file will be returned.

        :param path: The path to find
        :return: The absolute path to the file or None if not found
        """
        # Update paths from settings to make them editable runtime
        # This is only possible for FileSystemFinders
        if getattr(self, 'settings_attr', None):
            self.paths = getattr(settings, self.settings_attr)

        path_found = None

        for entry in self.paths:
            abspath = entry / path
            if abspath.exists():
                path_found = abspath

        return path_found

def find ( self , path : Path ) : if getattr ( self , 'settings_attr' , None ) : self . paths = getattr ( settings , self . settings_attr ) path_found = None for entry in self . paths : abspath = entry / path if abspath . exists ( ) : path_found = abspath return path_found

Find a file in the path . The file may exist in multiple paths . The last found file will be returned .


def update(self, aspect_ratio=None, fov=None, near=None, far=None):
        """
        Update the internal projection matrix based on current values
        or values passed in if specified.

        :param aspect_ratio: New aspect ratio
        :param fov: New field of view
        :param near: New near value
        :param far: New far value
        """
        self.aspect_ratio = aspect_ratio or self.aspect_ratio
        self.fov = fov or self.fov
        self.near = near or self.near
        self.far = far or self.far

        self.matrix = Matrix44.perspective_projection(self.fov, self.aspect_ratio, self.near, self.far)

def update ( self , aspect_ratio = None , fov = None , near = None , far = None ) : self . aspect_ratio = aspect_ratio or self . aspect_ratio self . fov = fov or self . fov self . near = near or self . near self . far = far or self . far self . matrix = Matrix44 . perspective_projection ( self . fov , self . aspect_ratio , self . near , self . far )

Update the internal projection matrix based on current values or values passed in if specified .


def draw(self, projection_matrix=None, camera_matrix=None, time=0):
        """
        Draw node and children

        :param projection_matrix: projection matrix (bytes)
        :param camera_matrix: camera_matrix (bytes)
        :param time: The current time
        """
        if self.mesh:
            self.mesh.draw(
                projection_matrix=projection_matrix,
                view_matrix=self.matrix_global_bytes,
                camera_matrix=camera_matrix,
                time=time
            )

        for child in self.children:
            child.draw(
                projection_matrix=projection_matrix,
                camera_matrix=camera_matrix,
                time=time
            )

def draw ( self , projection_matrix = None , camera_matrix = None , time = 0 ) : if self . mesh : self . mesh . draw ( projection_matrix = projection_matrix , view_matrix = self . matrix_global_bytes , camera_matrix = camera_matrix , time = time ) for child in self . children : child . draw ( projection_matrix = projection_matrix , camera_matrix = camera_matrix , time = time )


def calc_global_bbox(self, view_matrix, bbox_min, bbox_max):
        """Recursive calculation of scene bbox"""
        if self.matrix is not None:
            view_matrix = matrix44.multiply(self.matrix, view_matrix)

        if self.mesh:
            bbox_min, bbox_max = self.mesh.calc_global_bbox(view_matrix, bbox_min, bbox_max)

        for child in self.children:
            bbox_min, bbox_max = child.calc_global_bbox(view_matrix, bbox_min, bbox_max)

        return bbox_min, bbox_max

def calc_global_bbox ( self , view_matrix , bbox_min , bbox_max ) : if self . matrix is not None : view_matrix = matrix44 . multiply ( self . matrix , view_matrix ) if self . mesh : bbox_min , bbox_max = self . mesh . calc_global_bbox ( view_matrix , bbox_min , bbox_max ) for child in self . children : bbox_min , bbox_max = child . calc_global_bbox ( view_matrix , bbox_min , bbox_max ) return bbox_min , bbox_max


def swap_buffers(self):
        """
        Swaps buffers, incement the framecounter and pull events.
        """
        self.frames += 1
        glfw.swap_buffers(self.window)
        self.poll_events()

def swap_buffers ( self ) : self . frames += 1 glfw . swap_buffers ( self . window ) self . poll_events ( )

Swaps buffers incement the framecounter and pull events .


def resize(self, width, height):
        """
        Sets the new size and buffer size internally
        """
        self.width = width
        self.height = height
        self.buffer_width, self.buffer_height = glfw.get_framebuffer_size(self.window)
        self.set_default_viewport()

def resize ( self , width , height ) : self . width = width self . height = height self . buffer_width , self . buffer_height = glfw . get_framebuffer_size ( self . window ) self . set_default_viewport ( )


def check_glfw_version(self):
        """
        Ensure glfw library  version is compatible
        """
        print("glfw version: {} (python wrapper version {})".format(glfw.get_version(), glfw.__version__))
        if glfw.get_version() < self.min_glfw_version:
            raise ValueError("Please update glfw binaries to version {} or later".format(self.min_glfw_version))


def quad_2d(width, height, xpos=0.0, ypos=0.0) -> VAO:
    """
    Creates a 2D quad VAO using 2 triangles with normals and texture coordinates.

    Args:
        width (float): Width of the quad
        height (float): Height of the quad

    Keyword Args:
        xpos (float): Center position x
        ypos (float): Center position y

    Returns:
        A :py:class:`demosys.opengl.vao.VAO` instance.
    """
    pos = numpy.array([
        xpos - width / 2.0, ypos + height / 2.0, 0.0,
        xpos - width / 2.0, ypos - height / 2.0, 0.0,
        xpos + width / 2.0, ypos - height / 2.0, 0.0,
        xpos - width / 2.0, ypos + height / 2.0, 0.0,
        xpos + width / 2.0, ypos - height / 2.0, 0.0,
        xpos + width / 2.0, ypos + height / 2.0, 0.0,
    ], dtype=numpy.float32)

    normals = numpy.array([
        0.0, 0.0, 1.0,
        0.0, 0.0, 1.0,
        0.0, 0.0, 1.0,
        0.0, 0.0, 1.0,
        0.0, 0.0, 1.0,
        0.0, 0.0, 1.0,
    ], dtype=numpy.float32)

    uvs = numpy.array([
        0.0, 1.0,
        0.0, 0.0,
        1.0, 0.0,
        0.0, 1.0,
        1.0, 0.0,
        1.0, 1.0,
    ], dtype=numpy.float32)

    vao = VAO("geometry:quad", mode=moderngl.TRIANGLES)
    vao.buffer(pos, '3f', ["in_position"])
    vao.buffer(normals, '3f', ["in_normal"])
    vao.buffer(uvs, '2f', ["in_uv"])

    return vao

def quad_2d ( width , height , xpos = 0.0 , ypos = 0.0 ) -> VAO : pos = numpy . array ( [ xpos - width / 2.0 , ypos + height / 2.0 , 0.0 , xpos - width / 2.0 , ypos - height / 2.0 , 0.0 , xpos + width / 2.0 , ypos - height / 2.0 , 0.0 , xpos - width / 2.0 , ypos + height / 2.0 , 0.0 , xpos + width / 2.0 , ypos - height / 2.0 , 0.0 , xpos + width / 2.0 , ypos + height / 2.0 , 0.0 , ] , dtype = numpy . float32 ) normals = numpy . array ( [ 0.0 , 0.0 , 1.0 , 0.0 , 0.0 , 1.0 , 0.0 , 0.0 , 1.0 , 0.0 , 0.0 , 1.0 , 0.0 , 0.0 , 1.0 , 0.0 , 0.0 , 1.0 , ] , dtype = numpy . float32 ) uvs = numpy . array ( [ 0.0 , 1.0 , 0.0 , 0.0 , 1.0 , 0.0 , 0.0 , 1.0 , 1.0 , 0.0 , 1.0 , 1.0 , ] , dtype = numpy . float32 ) vao = VAO ( "geometry:quad" , mode = moderngl . TRIANGLES ) vao . buffer ( pos , '3f' , [ "in_position" ] ) vao . buffer ( normals , '3f' , [ "in_normal" ] ) vao . buffer ( uvs , '2f' , [ "in_uv" ] ) return vao

Creates a 2D quad VAO using 2 triangles with normals and texture coordinates .


def translate_buffer_format(vertex_format):
    """Translate the buffer format"""
    buffer_format = []
    attributes = []
    mesh_attributes = []

    if "T2F" in vertex_format:
        buffer_format.append("2f")
        attributes.append("in_uv")
        mesh_attributes.append(("TEXCOORD_0", "in_uv", 2))

    if "C3F" in vertex_format:
        buffer_format.append("3f")
        attributes.append("in_color")
        mesh_attributes.append(("NORMAL", "in_color", 3))

    if "N3F" in vertex_format:
        buffer_format.append("3f")
        attributes.append("in_normal")
        mesh_attributes.append(("NORMAL", "in_normal", 3))

    buffer_format.append("3f")
    attributes.append("in_position")
    mesh_attributes.append(("POSITION", "in_position", 3))

    return " ".join(buffer_format), attributes, mesh_attributes

def
translate_buffer_format
(
vertex_format
)
:
buffer_format
=
[
]
attributes
=
[
]
mesh_attributes
=
[
]
if
"T2F"
in
vertex_format
:
buffer_format
.
append
(
"2f"
)
attributes
.
append
(
"in_uv"
)
mesh_attributes
.
append
(
(
"TEXCOORD_0"
,
"in_uv"
,
2
)
)
if
"C3F"
in
vertex_format
:
buffer_format
.
append
(
"3f"
)
attributes
.
append
(
"in_color"
)
mesh_attributes
.
append
(
(
"NORMAL"
,
"in_color"
,
3
)
)
if
"N3F"
in
vertex_format
:
buffer_format
.
append
(
"3f"
)
attributes
.
append
(
"in_normal"
)
mesh_attributes
.
append
(
(
"NORMAL"
,
"in_normal"
,
3
)
)
buffer_format
.
append
(
"3f"
)
attributes
.
append
(
"in_position"
)
mesh_attributes
.
append
(
(
"POSITION"
,
"in_position"
,
3
)
)
return
" "
.
join
(
buffer_format
)
,
attributes
,
mesh_attributes


def stop(self) -> float:
        """
        Stop the timer

        Returns:
            The time the timer was stopped
        """
        self.stop_time = time.time()
        return self.stop_time - self.start_time - self.offset

def stop ( self ) -> float : self . stop_time = time . time ( ) return self . stop_time - self . start_time - self . offset


def set_time(self, value: float):
        """
        Set the current time. This can be used to jump in the timeline.

        Args:
            value (float): The new time
        """
        if value < 0:
            value = 0

        self.offset += self.get_time() - value

def set_time ( self , value : float ) : if value < 0 : value = 0 self . offset += self . get_time ( ) - value

Set the current time . This can be used to jump in the timeline .


def resolve_loader(self, meta: SceneDescription):
        """
        Resolve scene loader based on file extension
        """
        for loader_cls in self._loaders:
            if loader_cls.supports_file(meta):
                meta.loader_cls = loader_cls
                break
        else:
            raise ImproperlyConfigured(
                "Scene {} has no loader class registered. Check settings.SCENE_LOADERS".format(meta.path))


def on_resize(self, width, height):
        """
        Pyglet specific callback for window resize events.
        """
        self.width, self.height = width, height
        self.buffer_width, self.buffer_height = width, height
        self.resize(width, height)

def on_resize ( self , width , height ) : self . width , self . height = width , height self . buffer_width , self . buffer_height = width , height self . resize ( width , height )

Pyglet specific callback for window resize events .


def swap_buffers(self):
        """
        Swap buffers, increment frame counter and pull events
        """
        if not self.window.context:
            return

        self.frames += 1
        self.window.flip()
        self.window.dispatch_events()

def swap_buffers ( self ) : if not self . window . context : return self . frames += 1 self . window . flip ( ) self . window . dispatch_events ( )

Swap buffers increment frame counter and pull events


def sphere(radius=0.5, sectors=32, rings=16) -> VAO:
    """
    Creates a sphere.

    Keyword Args:
        radius (float): Radius or the sphere
        rings (int): number or horizontal rings
        sectors (int): number of vertical segments

    Returns:
        A :py:class:`demosys.opengl.vao.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

    vbo_vertices = numpy.array(vertices, dtype=numpy.float32)
    vbo_normals = numpy.array(normals, dtype=numpy.float32)
    vbo_uvs = numpy.array(uvs, dtype=numpy.float32)
    vbo_elements = numpy.array(indices, dtype=numpy.uint32)

    vao = VAO("sphere", mode=mlg.TRIANGLES)
    # VBOs
    vao.buffer(vbo_vertices, '3f', ['in_position'])
    vao.buffer(vbo_normals, '3f', ['in_normal'])
    vao.buffer(vbo_uvs, '2f', ['in_uv'])
    vao.index_buffer(vbo_elements, index_element_size=4)

    return vao

def sphere ( radius = 0.5 , sectors = 32 , rings = 16 ) -> VAO : 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 vbo_vertices = numpy . array ( vertices , dtype = numpy . float32 ) vbo_normals = numpy . array ( normals , dtype = numpy . float32 ) vbo_uvs = numpy . array ( uvs , dtype = numpy . float32 ) vbo_elements = numpy . array ( indices , dtype = numpy . uint32 ) vao = VAO ( "sphere" , mode = mlg . TRIANGLES ) vao . buffer ( vbo_vertices , '3f' , [ 'in_position' ] ) vao . buffer ( vbo_normals , '3f' , [ 'in_normal' ] ) vao . buffer ( vbo_uvs , '2f' , [ 'in_uv' ] ) vao . index_buffer ( vbo_elements , index_element_size = 4 ) return vao


def swap_buffers(self):
        """
        Headless window currently don't support double buffering.
        We only increment the frame counter here.
        """
        self.frames += 1

        if self.headless_frames and self.frames >= self.headless_frames:
            self.close()

def swap_buffers ( self ) : self . frames += 1 if self . headless_frames and self . frames >= self . headless_frames : self . close ( )

Headless window currently don t support double buffering . We only increment the frame counter here .


def load(self, meta: ResourceDescription) -> Any:
        """
        Loads a resource or return existing one

        :param meta: The resource description
        """
        self._check_meta(meta)
        self.resolve_loader(meta)
        return meta.loader_cls(meta).load()

def load ( self , meta : ResourceDescription ) -> Any : self . _check_meta ( meta ) self . resolve_loader ( meta ) return meta . loader_cls ( meta ) . load ( )


def load_pool(self):
        """
        Loads all the data files using the configured finders.
        """
        for meta in self._resources:
            resource = self.load(meta)
            yield meta, resource

        self._resources = []

def load_pool ( self ) : for meta in self . _resources : resource = self . load ( meta ) yield meta , resource self . _resources = [ ]

Loads all the data files using the configured finders .


def resolve_loader(self, meta: ResourceDescription):
        """
        Attempts to assign a loader class to a resource description

        :param meta: The resource description instance
        """
        meta.loader_cls = self.get_loader(meta, raise_on_error=True)

def resolve_loader ( self , meta : ResourceDescription ) : meta . loader_cls = self . get_loader ( meta , raise_on_error = True )


def get_loader(self, meta: ResourceDescription, raise_on_error=False) -> BaseLoader:
        """
        Attempts to get a loader

        :param meta: The resource description instance
        :param raise_on_error: Raise ImproperlyConfigured if the loader cannot be resolved
        :returns: The requested loader class
        """
        for loader in self._loaders:
            if loader.name == meta.loader:
                return loader

        if raise_on_error:
            raise ImproperlyConfigured(
                "Resource has invalid loader '{}': {}\nAvailiable loaders: {}".format(
                    meta.loader, meta, [loader.name for loader in self._loaders]))


def resize(self, width, height):
        """
        Pyqt specific resize callback.
        """
        if not self.fbo:
            return

        # pyqt reports sizes in actual buffer size
        self.width = width // self.widget.devicePixelRatio()
        self.height = height // self.widget.devicePixelRatio()
        self.buffer_width = width
        self.buffer_height = height

        super().resize(width, height)

def resize ( self , width , height ) : if not self . fbo : return self . width = width // self . widget . devicePixelRatio ( ) self . height = height // self . widget . devicePixelRatio ( ) self . buffer_width = width self . buffer_height = height super ( ) . resize ( width , height )


def draw(self, current_time, frame_time):
        """
        Draws a frame. Internally it calls the
        configured timeline's draw method.

        Args:
            current_time (float): The current time (preferrably always from the configured timer class)
            frame_time (float): The duration of the previous frame in seconds
        """
        self.set_default_viewport()
        self.timeline.draw(current_time, frame_time, self.fbo)

def draw ( self , current_time , frame_time ) : self . set_default_viewport ( ) self . timeline . draw ( current_time , frame_time , self . fbo )

Draws a frame . Internally it calls the configured timeline s draw method .


def clear(self):
        """
        Clear the window buffer
        """
        self.ctx.fbo.clear(
            red=self.clear_color[0],
            green=self.clear_color[1],
            blue=self.clear_color[2],
            alpha=self.clear_color[3],
            depth=self.clear_depth,
        )

def clear ( self ) : self . ctx . fbo . clear ( red = self . clear_color [ 0 ] , green = self . clear_color [ 1 ] , blue = self . clear_color [ 2 ] , alpha = self . clear_color [ 3 ] , depth = self . clear_depth , )


def clear_values(self, red=0.0, green=0.0, blue=0.0, alpha=0.0, depth=1.0):
        """
        Sets the clear values for the window buffer.

        Args:
            red (float): red compoent
            green (float): green compoent
            blue (float): blue compoent
            alpha (float): alpha compoent
            depth (float): depth value
        """
        self.clear_color = (red, green, blue, alpha)
        self.clear_depth = depth

def clear_values ( self , red = 0.0 , green = 0.0 , blue = 0.0 , alpha = 0.0 , depth = 1.0 ) : self . clear_color = ( red , green , blue , alpha ) self . clear_depth = depth

Sets the clear values for the window buffer .


def keyboard_event(self, key, action, modifier):
        """
        Handles the standard keyboard events such as camera movements,
        taking a screenshot, closing the window etc.

        Can be overriden add new keyboard events. Ensure this method
        is also called if you want to keep the standard features.

        Arguments:
            key: The key that was pressed or released
            action: The key action. Can be `ACTION_PRESS` or `ACTION_RELEASE`
            modifier: Modifiers such as holding shift or ctrl
        """
        # The well-known standard key for quick exit
        if key == self.keys.ESCAPE:
            self.close()
            return

        # Toggle pause time
        if key == self.keys.SPACE and action == self.keys.ACTION_PRESS:
            self.timer.toggle_pause()

        # Camera movement
        # Right
        if key == self.keys.D:
            if action == self.keys.ACTION_PRESS:
                self.sys_camera.move_right(True)
            elif action == self.keys.ACTION_RELEASE:
                self.sys_camera.move_right(False)
        # Left
        elif key == self.keys.A:
            if action == self.keys.ACTION_PRESS:
                self.sys_camera.move_left(True)
            elif action == self.keys.ACTION_RELEASE:
                self.sys_camera.move_left(False)
        # Forward
        elif key == self.keys.W:
            if action == self.keys.ACTION_PRESS:
                self.sys_camera.move_forward(True)
            if action == self.keys.ACTION_RELEASE:
                self.sys_camera.move_forward(False)
        # Backwards
        elif key == self.keys.S:
            if action == self.keys.ACTION_PRESS:
                self.sys_camera.move_backward(True)
            if action == self.keys.ACTION_RELEASE:
                self.sys_camera.move_backward(False)

        # UP
        elif key == self.keys.Q:
            if action == self.keys.ACTION_PRESS:
                self.sys_camera.move_down(True)
            if action == self.keys.ACTION_RELEASE:
                self.sys_camera.move_down(False)

        # Down
        elif key == self.keys.E:
            if action == self.keys.ACTION_PRESS:
                self.sys_camera.move_up(True)
            if action == self.keys.ACTION_RELEASE:
                self.sys_camera.move_up(False)

        # Screenshots
        if key == self.keys.X and action == self.keys.ACTION_PRESS:
            screenshot.create()

        if key == self.keys.R and action == self.keys.ACTION_PRESS:
            project.instance.reload_programs()

        if key == self.keys.RIGHT and action == self.keys.ACTION_PRESS:
            self.timer.set_time(self.timer.get_time() + 10.0)

        if key == self.keys.LEFT and action == self.keys.ACTION_PRESS:
            self.timer.set_time(self.timer.get_time() - 10.0)

        # Forward the event to the timeline
        self.timeline.key_event(key, action, modifier)

def keyboard_event ( self , key , action , modifier ) : if key == self . keys . ESCAPE : self . close ( ) return if key == self . keys . SPACE and action == self . keys . ACTION_PRESS : self . timer . toggle_pause ( ) if key == self . keys . D : if action == self . keys . ACTION_PRESS : self . sys_camera . move_right ( True ) elif action == self . keys . ACTION_RELEASE : self . sys_camera . move_right ( False ) elif key == self . keys . A : if action == self . keys . ACTION_PRESS : self . sys_camera . move_left ( True ) elif action == self . keys . ACTION_RELEASE : self . sys_camera . move_left ( False ) elif key == self . keys . W : if action == self . keys . ACTION_PRESS : self . sys_camera . move_forward ( True ) if action == self . keys . ACTION_RELEASE : self . sys_camera . move_forward ( False ) elif key == self . keys . S : if action == self . keys . ACTION_PRESS : self . sys_camera . move_backward ( True ) if action == self . keys . ACTION_RELEASE : self . sys_camera . move_backward ( False ) elif key == self . keys . Q : if action == self . keys . ACTION_PRESS : self . sys_camera . move_down ( True ) if action == self . keys . ACTION_RELEASE : self . sys_camera . move_down ( False ) elif key == self . keys . E : if action == self . keys . ACTION_PRESS : self . sys_camera . move_up ( True ) if action == self . keys . ACTION_RELEASE : self . sys_camera . move_up ( False ) if key == self . keys . X and action == self . keys . ACTION_PRESS : screenshot . create ( ) if key == self . keys . R and action == self . keys . ACTION_PRESS : project . instance . reload_programs ( ) if key == self . keys . RIGHT and action == self . keys . ACTION_PRESS : self . timer . set_time ( self . timer . get_time ( ) + 10.0 ) if key == self . keys . LEFT and action == self . keys . ACTION_PRESS : self . timer . set_time ( self . timer . get_time ( ) - 10.0 ) self . timeline . key_event ( key , action , modifier )

Handles the standard keyboard events such as camera movements taking a screenshot closing the window etc .


def cursor_event(self, x, y, dx, dy):
        """
        The standard mouse movement event method.
        Can be overriden to add new functionality.
        By default this feeds the system camera with new values.

        Args:
            x: The current mouse x position
            y: The current mouse y position
            dx: Delta x postion (x position difference from the previous event)
            dy: Delta y postion (y position difference from the previous event)
        """
        self.sys_camera.rot_state(x, y)

def cursor_event ( self , x , y , dx , dy ) : self . sys_camera . rot_state ( x , y )

The standard mouse movement event method . Can be overriden to add new functionality . By default this feeds the system camera with new values .


def set_default_viewport(self):
        """
        Calculates the viewport based on the configured aspect ratio in settings.
        Will add black borders if the window do not match the viewport.
        """
        # The expected height with the current viewport width
        expected_height = int(self.buffer_width / self.aspect_ratio)

        # How much positive or negative y padding
        blank_space = self.buffer_height - expected_height
        self.fbo.viewport = (0, blank_space // 2, self.buffer_width, expected_height)

def set_default_viewport ( self ) : expected_height = int ( self . buffer_width / self . aspect_ratio ) blank_space = self . buffer_height - expected_height self . fbo . viewport = ( 0 , blank_space // 2 , self . buffer_width , expected_height )

Calculates the viewport based on the configured aspect ratio in settings . Will add black borders if the window do not match the viewport .


def start(self):
        """Start the timer"""
        self.music.start()
        if not self.start_paused:
            self.rocket.start()

def start ( self ) : self . music . start ( ) if not self . start_paused : self . rocket . start ( )


def toggle_pause(self):
        """Toggle pause mode"""
        self.controller.playing = not self.controller.playing
        self.music.toggle_pause()

def toggle_pause ( self ) : self . controller . playing = not self . controller . playing self . music . toggle_pause ( )


def supports_file(cls, meta):
        """Check if the loader has a supported file extension"""
        path = Path(meta.path)

        for ext in cls.file_extensions:
            if path.suffixes[:len(ext)] == ext:
                return True

        return False

def supports_file ( cls , meta ) : path = Path ( meta . path ) for ext in cls . file_extensions : if path . suffixes [ : len ( ext ) ] == ext : return True return False


def get(self, name) -> Track:
        """
        Get or create a Track object.

        :param name: Name of the track
        :return: Track object
        """
        name = name.lower()
        track = self.track_map.get(name)
        if not track:
            track = Track(name)
            self.tacks.append(track)
            self.track_map[name] = track
        return track

def get ( self , name ) -> Track : name = name . lower ( ) track = self . track_map . get ( name ) if not track : track = Track ( name ) self . tacks . append ( track ) self . track_map [ name ] = track return track


def find_commands(command_dir: str) -> List[str]:
    """
    Get all command names in the a folder

    :return: List of commands names
    """
    if not command_dir:
        return []

    return [name for _, name, is_pkg in pkgutil.iter_modules([command_dir])
            if not is_pkg and not name.startswith('_')]

def find_commands ( command_dir : str ) -> List [ str ] : if not command_dir : return [ ] return [ name for _ , name , is_pkg in pkgutil . iter_modules ( [ command_dir ] ) if not is_pkg and not name . startswith ( '_' ) ]


def update(self, **kwargs):
        """Override settings values"""
        for name, value in kwargs.items():
            setattr(self, name, value)

def update ( self , ** kwargs ) : for name , value in kwargs . items ( ) : setattr ( self , name , value )


def add_program_dir(self, directory):
        """Hack in program directory"""
        dirs = list(self.PROGRAM_DIRS)
        dirs.append(directory)
        self.PROGRAM_DIRS = dirs

def add_program_dir ( self , directory ) : dirs = list ( self . PROGRAM_DIRS ) dirs . append ( directory ) self . PROGRAM_DIRS = dirs


def add_texture_dir(self, directory):
        """Hack in texture directory"""
        dirs = list(self.TEXTURE_DIRS)
        dirs.append(directory)
        self.TEXTURE_DIRS = dirs

def add_texture_dir ( self , directory ) : dirs = list ( self . TEXTURE_DIRS ) dirs . append ( directory ) self . TEXTURE_DIRS = dirs


def add_data_dir(self, directory):
        """Hack in a data directory"""
        dirs = list(self.DATA_DIRS)
        dirs.append(directory)
        self.DATA_DIRS = dirs

def add_data_dir ( self , directory ) : dirs = list ( self . DATA_DIRS ) dirs . append ( directory ) self . DATA_DIRS = dirs


def render(self, program: moderngl.Program, mode=None, vertices=-1, first=0, instances=1):
        """
        Render the VAO.

        Args:
            program: The ``moderngl.Program``

        Keyword Args:
            mode: Override the draw mode (``TRIANGLES`` etc)
            vertices (int): The number of vertices to transform
            first (int): The index of the first vertex to start with
            instances (int): The number of instances
        """
        vao = self.instance(program)

        if mode is None:
            mode = self.mode

        vao.render(mode, vertices=vertices, first=first, instances=instances)

def render ( self , program : moderngl . Program , mode = None , vertices = - 1 , first = 0 , instances = 1 ) : vao = self . instance ( program ) if mode is None : mode = self . mode vao . render ( mode , vertices = vertices , first = first , instances = instances )


def transform(self, program: moderngl.Program, buffer: moderngl.Buffer,
                  mode=None, vertices=-1, first=0, instances=1):
        """
        Transform vertices. Stores the output in a single buffer.

        Args:
            program: The ``moderngl.Program``
            buffer: The ``moderngl.buffer`` to store the output

        Keyword Args:
            mode: Draw mode (for example ``moderngl.POINTS``)
            vertices (int): The number of vertices to transform
            first (int): The index of the first vertex to start with
            instances (int): The number of instances
        """
        vao = self.instance(program)

        if mode is None:
            mode = self.mode

        vao.transform(buffer, mode=mode, vertices=vertices, first=first, instances=instances)

def transform ( self , program : moderngl . Program , buffer : moderngl . Buffer , mode = None , vertices = - 1 , first = 0 , instances = 1 ) : vao = self . instance ( program ) if mode is None : mode = self . mode vao . transform ( buffer , mode = mode , vertices = vertices , first = first , instances = instances )

Transform vertices . Stores the output in a single buffer .


def index_buffer(self, buffer, index_element_size=4):
        """
        Set the index buffer for this VAO

        Args:
            buffer: ``moderngl.Buffer``, ``numpy.array`` or ``bytes``

        Keyword Args:
            index_element_size (int): Byte size of each element. 1, 2 or 4
        """
        if not type(buffer) in [moderngl.Buffer, numpy.ndarray, bytes]:
            raise VAOError("buffer parameter must be a moderngl.Buffer, numpy.ndarray or bytes instance")

        if isinstance(buffer, numpy.ndarray):
            buffer = self.ctx.buffer(buffer.tobytes())

        if isinstance(buffer, bytes):
            buffer = self.ctx.buffer(data=buffer)

        self._index_buffer = buffer
        self._index_element_size = index_element_size


def instance(self, program: moderngl.Program) -> moderngl.VertexArray:
        """
        Obtain the ``moderngl.VertexArray`` instance for the program.
        The instance is only created once and cached internally.

        Returns: ``moderngl.VertexArray`` instance
        """
        vao = self.vaos.get(program.glo)
        if vao:
            return vao

        program_attributes = [name for name, attr in program._members.items() if isinstance(attr, moderngl.Attribute)]

        # Make sure all attributes are covered
        for attrib_name in program_attributes:
            # Ignore built in attributes for now
            if attrib_name.startswith('gl_'):
                continue

            # Do we have a buffer mapping to this attribute?
            if not sum(buffer.has_attribute(attrib_name) for buffer in self.buffers):
                raise VAOError("VAO {} doesn't have attribute {} for program {}".format(
                    self.name, attrib_name, program.name))

        vao_content = []

        # Pick out the attributes we can actually map
        for buffer in self.buffers:
            content = buffer.content(program_attributes)
            if content:
                vao_content.append(content)

        # Any attribute left is not accounted for
        if program_attributes:
            for attrib_name in program_attributes:
                if attrib_name.startswith('gl_'):
                    continue

                raise VAOError("Did not find a buffer mapping for {}".format([n for n in program_attributes]))

        # Create the vao
        if self._index_buffer:
            vao = context.ctx().vertex_array(program, vao_content,
                                             self._index_buffer, self._index_element_size)
        else:
            vao = context.ctx().vertex_array(program, vao_content)

        self.vaos[program.glo] = vao
        return vao

Obtain the moderngl . VertexArray instance for the program . The instance is only created once and cached internally .


def release(self, buffer=True):
        """
        Destroy the vao object

        Keyword Args:
            buffers (bool): also release buffers
        """
        for key, vao in self.vaos:
            vao.release()

        if buffer:
            for buff in self.buffers:
                buff.buffer.release()

            if self._index_buffer:
                self._index_buffer.release()

def release ( self , buffer = True ) : for key , vao in self . vaos : vao . release ( ) if buffer : for buff in self . buffers : buff . buffer . release ( ) if self . _index_buffer : self . _index_buffer . release ( )


def draw(self, mesh, projection_matrix=None, view_matrix=None, camera_matrix=None, time=0):
        """
        Draw code for the mesh. Should be overriden.

        :param projection_matrix: projection_matrix (bytes)
        :param view_matrix: view_matrix (bytes)
        :param camera_matrix: camera_matrix (bytes)
        :param time: The current time
        """
        self.program["m_proj"].write(projection_matrix)
        self.program["m_mv"].write(view_matrix)
        mesh.vao.render(self.program)

def draw ( self , mesh , projection_matrix = None , view_matrix = None , camera_matrix = None , time = 0 ) : self . program [ "m_proj" ] . write ( projection_matrix ) self . program [ "m_mv" ] . write ( view_matrix ) mesh . vao . render ( self . program )

Draw code for the mesh . Should be overriden .


def parse_package_string(path):
    """
    Parse the effect package string.
    Can contain the package python path or path to effect class in an effect package.

    Examples::

        # Path to effect pacakge
        examples.cubes

        # Path to effect class
        examples.cubes.Cubes

    Args:
        path: python path to effect package. May also include effect class name.

    Returns:
        tuple: (package_path, effect_class)
    """
    parts = path.split('.')

    # Is the last entry in the path capitalized?
    if parts[-1][0].isupper():
        return ".".join(parts[:-1]), parts[-1]

    return path, ""

def parse_package_string ( path ) : parts = path . split ( '.' ) if parts [ - 1 ] [ 0 ] . isupper ( ) : return "." . join ( parts [ : - 1 ] ) , parts [ - 1 ] return path , ""

Parse the effect package string . Can contain the package python path or path to effect class in an effect package .


def get_dirs(self) -> List[str]:
        """
        Get all effect directories for registered effects.
        """
        for package in self.packages:
            yield os.path.join(package.path, 'resources')

def get_dirs ( self ) -> List [ str ] : for package in self . packages : yield os . path . join ( package . path , 'resources' )

Get all effect directories for registered effects .


def get_effect_resources(self) -> List[Any]:
        """
        Get all resources registed in effect packages.
        These are typically located in ``resources.py``
        """
        resources = []
        for package in self.packages:
            resources.extend(package.resources)

        return resources

def get_effect_resources ( self ) -> List [ Any ] : resources = [ ] for package in self . packages : resources . extend ( package . resources ) return resources

Get all resources registed in effect packages . These are typically located in resources . py


def add_package(self, name):
        """
        Registers a single package

        :param name: (str) The effect package to add
        """
        name, cls_name = parse_package_string(name)

        if name in self.package_map:
            return

        package = EffectPackage(name)
        package.load()

        self.packages.append(package)
        self.package_map[package.name] = package

        # Load effect package dependencies
        self.polulate(package.effect_packages)

def add_package ( self , name ) : name , cls_name = parse_package_string ( name ) if name in self . package_map : return package = EffectPackage ( name ) package . load ( ) self . packages . append ( package ) self . package_map [ package . name ] = package self . polulate ( package . effect_packages )


def get_package(self, name) -> 'EffectPackage':
        """
        Get a package by python path. Can also contain path to an effect.

        Args:
            name (str): Path to effect package or effect

        Returns:
            The requested EffectPackage

        Raises:
            EffectError when no package is found
        """
        name, cls_name = parse_package_string(name)

        try:
            return self.package_map[name]
        except KeyError:
            raise EffectError("No package '{}' registered".format(name))

def
get_package
(
self
,
name
)
->
'EffectPackage'
:
name
,
cls_name
=
parse_package_string
(
name
)
try
:
return
self
.
package_map
[
name
]
except
KeyError
:
raise
EffectError
(
"No package '{}' registered"
.
format
(
name
)
)

Get a package by python path . Can also contain path to an effect .


def find_effect_class(self, path) -> Type[Effect]:
        """
        Find an effect class by class name or full python path to class

        Args:
            path (str): effect class name or full python path to effect class

        Returns:
            Effect class

        Raises:
            EffectError if no class is found
        """
        package_name, class_name = parse_package_string(path)

        if package_name:
            package = self.get_package(package_name)
            return package.find_effect_class(class_name, raise_for_error=True)

        for package in self.packages:
            effect_cls = package.find_effect_class(class_name)
            if effect_cls:
                return effect_cls

        raise EffectError("No effect class '{}' found in any packages".format(class_name))


def runnable_effects(self) -> List[Type[Effect]]:
        """Returns the runnable effect in the package"""
        return [cls for cls in self.effect_classes if cls.runnable]

def runnable_effects ( self ) -> List [ Type [ Effect ] ] : return [ cls for cls in self . effect_classes if cls . runnable ]


def load_package(self):
        """FInd the effect package"""
        try:
            self.package = importlib.import_module(self.name)
        except ModuleNotFoundError:
            raise ModuleNotFoundError("Effect package '{}' not found.".format(self.name))

def
load_package
(
self
)
:
try
:
self
.
package
=
importlib
.
import_module
(
self
.
name
)
except
ModuleNotFoundError
:
raise
ModuleNotFoundError
(
"Effect package '{}' not found."
.
format
(
self
.
name
)
)


def load_effects_classes(self):
        """Iterate the module attributes picking out effects"""
        self.effect_classes = []

        for _, cls in inspect.getmembers(self.effect_module):
            if inspect.isclass(cls):
                if cls == Effect:
                    continue

                if issubclass(cls, Effect):
                    self.effect_classes.append(cls)
                    self.effect_class_map[cls.__name__] = cls
                    cls._name = "{}.{}".format(self.effect_module_name, cls.__name__)

def load_effects_classes ( self ) : self . effect_classes = [ ] for _ , cls in inspect . getmembers ( self . effect_module ) : if inspect . isclass ( cls ) : if cls == Effect : continue if issubclass ( cls , Effect ) : self . effect_classes . append ( cls ) self . effect_class_map [ cls . __name__ ] = cls cls . _name = "{}.{}" . format ( self . effect_module_name , cls . __name__ )


def load_resource_module(self):
        """Fetch the resource list"""
        # Attempt to load the dependencies module
        try:
            name = '{}.{}'.format(self.name, 'dependencies')
            self.dependencies_module = importlib.import_module(name)
        except ModuleNotFoundError as err:
            raise EffectError(
                (
                    "Effect package '{}' has no 'dependencies' module or the module has errors. "
                    "Forwarded error from importlib: {}"
                ).format(self.name, err))

        # Fetch the resource descriptions
        try:
            self.resources = getattr(self.dependencies_module, 'resources')
        except AttributeError:
            raise EffectError("Effect dependencies module '{}' has no 'resources' attribute".format(name))

        if not isinstance(self.resources, list):
            raise EffectError(
                "Effect dependencies module '{}': 'resources' is of type {} instead of a list".format(
                    name, type(self.resources)))

        # Fetch the effect class list
        try:
            self.effect_packages = getattr(self.dependencies_module, 'effect_packages')
        except AttributeError:
            raise EffectError("Effect dependencies module '{}' has 'effect_packages' attribute".format(name))

        if not isinstance(self.effect_packages, list):
            raise EffectError(
                "Effect dependencies module '{}': 'effect_packages' is of type {} instead of a list".format(
                    name, type(self.effects)))


def draw(self, time, frametime, target):
        """
        Fetch track value for every runnable effect.
        If the value is > 0.5 we draw it.
        """
        for effect in self.effects:
            value = effect.rocket_timeline_track.time_value(time)
            if value > 0.5:
                effect.draw(time, frametime, target)

def draw ( self , time , frametime , target ) : for effect in self . effects : value = effect . rocket_timeline_track . time_value ( time ) if value > 0.5 : effect . draw ( time , frametime , target )

Fetch track value for every runnable effect . If the value is > 0 . 5 we draw it .


def load(self):
        """Load a 2d texture"""
        self._open_image()

        components, data = image_data(self.image)

        texture = self.ctx.texture(
            self.image.size,
            components,
            data,
        )
        texture.extra = {'meta': self.meta}

        if self.meta.mipmap:
            texture.build_mipmaps()

        self._close_image()

        return texture

def load ( self ) : self . _open_image ( ) components , data = image_data ( self . image ) texture = self . ctx . texture ( self . image . size , components , data , ) texture . extra = { 'meta' : self . meta } if self . meta . mipmap : texture . build_mipmaps ( ) self . _close_image ( ) return texture


def from_single(cls, meta: ProgramDescription, source: str):
        """Initialize a single glsl string containing all shaders"""
        instance = cls(meta)
        instance.vertex_source = ShaderSource(
            VERTEX_SHADER,
            meta.path or meta.vertex_shader,
            source
        )

        if GEOMETRY_SHADER in source:
            instance.geometry_source = ShaderSource(
                GEOMETRY_SHADER,
                meta.path or meta.geometry_shader,
                source,
            )

        if FRAGMENT_SHADER in source:
            instance.fragment_source = ShaderSource(
                FRAGMENT_SHADER,
                meta.path or meta.fragment_shader,
                source,
            )

        if TESS_CONTROL_SHADER in source:
            instance.tess_control_source = ShaderSource(
                TESS_CONTROL_SHADER,
                meta.path or meta.tess_control_shader,
                source,
            )

        if TESS_EVALUATION_SHADER in source:
            instance.tess_evaluation_source = ShaderSource(
                TESS_EVALUATION_SHADER,
                meta.path or meta.tess_evaluation_shader,
                source,
            )

        return instance

def from_single ( cls , meta : ProgramDescription , source : str ) : instance = cls ( meta ) instance . vertex_source = ShaderSource ( VERTEX_SHADER , meta . path or meta . vertex_shader , source ) if GEOMETRY_SHADER in source : instance . geometry_source = ShaderSource ( GEOMETRY_SHADER , meta . path or meta . geometry_shader , source , ) if FRAGMENT_SHADER in source : instance . fragment_source = ShaderSource ( FRAGMENT_SHADER , meta . path or meta . fragment_shader , source , ) if TESS_CONTROL_SHADER in source : instance . tess_control_source = ShaderSource ( TESS_CONTROL_SHADER , meta . path or meta . tess_control_shader , source , ) if TESS_EVALUATION_SHADER in source : instance . tess_evaluation_source = ShaderSource ( TESS_EVALUATION_SHADER , meta . path or meta . tess_evaluation_shader , source , ) return instance


def from_separate(cls, meta: ProgramDescription, vertex_source, geometry_source=None, fragment_source=None,
                      tess_control_source=None, tess_evaluation_source=None):
        """Initialize multiple shader strings"""
        instance = cls(meta)
        instance.vertex_source = ShaderSource(
            VERTEX_SHADER,
            meta.path or meta.vertex_shader,
            vertex_source,
        )

        if geometry_source:
            instance.geometry_source = ShaderSource(
                GEOMETRY_SHADER,
                meta.path or meta.geometry_shader,
                geometry_source,
            )

        if fragment_source:
            instance.fragment_source = ShaderSource(
                FRAGMENT_SHADER,
                meta.path or meta.fragment_shader,
                fragment_source,
            )

        if tess_control_source:
            instance.tess_control_source = ShaderSource(
                TESS_CONTROL_SHADER,
                meta.path or meta.tess_control_shader,
                tess_control_source,
            )

        if tess_evaluation_source:
            instance.tess_evaluation_source = ShaderSource(
                TESS_EVALUATION_SHADER,
                meta.path or meta.tess_control_shader,
                tess_evaluation_source,
            )

        return instance

def from_separate ( cls , meta : ProgramDescription , vertex_source , geometry_source = None , fragment_source = None , tess_control_source = None , tess_evaluation_source = None ) : instance = cls ( meta ) instance . vertex_source = ShaderSource ( VERTEX_SHADER , meta . path or meta . vertex_shader , vertex_source , ) if geometry_source : instance . geometry_source = ShaderSource ( GEOMETRY_SHADER , meta . path or meta . geometry_shader , geometry_source , ) if fragment_source : instance . fragment_source = ShaderSource ( FRAGMENT_SHADER , meta . path or meta . fragment_shader , fragment_source , ) if tess_control_source : instance . tess_control_source = ShaderSource ( TESS_CONTROL_SHADER , meta . path or meta . tess_control_shader , tess_control_source , ) if tess_evaluation_source : instance . tess_evaluation_source = ShaderSource ( TESS_EVALUATION_SHADER , meta . path or meta . tess_control_shader , tess_evaluation_source , ) return instance


def print(self):
        """Print the shader lines"""
        print("---[ START {} ]---".format(self.name))

        for i, line in enumerate(self.lines):
            print("{}: {}".format(str(i).zfill(3), line))

        print("---[ END {} ]---".format(self.name))

def
print
(
self
)
:
print
(
"---[ START {} ]---"
.
format
(
self
.
name
)
)
for
i
,
line
in
enumerate
(
self
.
lines
)
:
print
(
"{}: {}"
.
format
(
str
(
i
)
.
zfill
(
3
)
,
line
)
)
print
(
"---[ END {} ]---"
.
format
(
self
.
name
)
)


def load(self):
        """
        Loads this project instance
        """
        self.create_effect_classes()

        self._add_resource_descriptions_to_pools(self.create_external_resources())
        self._add_resource_descriptions_to_pools(self.create_resources())

        for meta, resource in resources.textures.load_pool():
            self._textures[meta.label] = resource

        for meta, resource in resources.programs.load_pool():
            self._programs[meta.label] = resource

        for meta, resource in resources.scenes.load_pool():
            self._scenes[meta.label] = resource

        for meta, resource in resources.data.load_pool():
            self._data[meta.label] = resource

        self.create_effect_instances()
        self.post_load()

def load ( self ) : self . create_effect_classes ( ) self . _add_resource_descriptions_to_pools ( self . create_external_resources ( ) ) self . _add_resource_descriptions_to_pools ( self . create_resources ( ) ) for meta , resource in resources . textures . load_pool ( ) : self . _textures [ meta . label ] = resource for meta , resource in resources . programs . load_pool ( ) : self . _programs [ meta . label ] = resource for meta , resource in resources . scenes . load_pool ( ) : self . _scenes [ meta . label ] = resource for meta , resource in resources . data . load_pool ( ) : self . _data [ meta . label ] = resource self . create_effect_instances ( ) self . post_load ( )


def _add_resource_descriptions_to_pools(self, meta_list):
        """
        Takes a list of resource descriptions adding them
        to the resource pool they belong to scheduling them for loading.
        """
        if not meta_list:
            return

        for meta in meta_list:
            getattr(resources, meta.resource_type).add(meta)

def _add_resource_descriptions_to_pools ( self , meta_list ) : if not meta_list : return for meta in meta_list : getattr ( resources , meta . resource_type ) . add ( meta )

Takes a list of resource descriptions adding them to the resource pool they belong to scheduling them for loading .


def reload_programs(self):
        """
        Reload all shader programs with the reloadable flag set
        """
        print("Reloading programs:")
        for name, program in self._programs.items():
            if getattr(program, 'program', None):
                print(" - {}".format(program.meta.label))
                program.program = resources.programs.load(program.meta)

def
reload_programs
(
self
)
:
print
(
"Reloading programs:"
)
for
name
,
program
in
self
.
_programs
.
items
(
)
:
if
getattr
(
program
,
'program'
,
)
:
print
(
" - {}"
.
format
(
program
.
meta
.
label
)
)
program
.
program
=
resources
.
programs
.
load
(
program
.
meta
)


def image_data(image):
    """Get components and bytes for an image"""
    # NOTE: We might want to check the actual image.mode
    #       and convert to an acceptable format.
    #       At the moment we load the data as is.
    data = image.tobytes()
    components = len(data) // (image.size[0] * image.size[1])
    return components, data

def image_data ( image ) : data = image . tobytes ( ) components = len ( data ) // ( image . size [ 0 ] * image . size [ 1 ] ) return components , data


def _find_last_of(self, path, finders):
        """Find the last occurance of the file in finders"""
        found_path = None
        for finder in finders:
            result = finder.find(path)
            if result:
                found_path = result

        return found_path

def _find_last_of ( self , path , finders ) : found_path = None for finder in finders : result = finder . find ( path ) if result : found_path = result return found_path


def initial_sanity_check(self):
        """Checks if we can create the project"""
        # Check for python module collision
        self.try_import(self.project_name)

        # Is the name a valid identifier?
        self.validate_name(self.project_name)

        # Make sure we don't mess with existing directories
        if os.path.exists(self.project_name):
            print("Directory {} already exist. Aborting.".format(self.project_name))
            return False

        if os.path.exists('manage.py'):
            print("A manage.py file already exist in the current directory. Aborting.")
            return False

        return True


def create_entrypoint(self):
        """Write manage.py in the current directory"""
        with open(os.path.join(self.template_dir, 'manage.py'), 'r') as fd:
            data = fd.read().format(project_name=self.project_name)

        with open('manage.py', 'w') as fd:
            fd.write(data)

        os.chmod('manage.py', 0o777)

def create_entrypoint ( self ) : with open ( os . path . join ( self . template_dir , 'manage.py' ) , 'r' ) as fd : data = fd . read ( ) . format ( project_name = self . project_name ) with open ( 'manage.py' , 'w' ) as fd : fd . write ( data ) os . chmod ( 'manage.py' , 0o777 )

Write manage . py in the current directory


def get_template_dir(self):
        """Returns the absolute path to template directory"""
        directory = os.path.dirname(os.path.abspath(__file__))
        directory = os.path.dirname(os.path.dirname(directory))
        directory = os.path.join(directory, 'project_template')
        return directory

def get_template_dir ( self ) : directory = os . path . dirname ( os . path . abspath ( __file__ ) ) directory = os . path . dirname ( os . path . dirname ( directory ) ) directory = os . path . join ( directory , 'project_template' ) return directory


def resolve_loader(self, meta: ProgramDescription):
        """
        Resolve program loader
        """
        if not meta.loader:
            meta.loader = 'single' if meta.path else 'separate'

        for loader_cls in self._loaders:
            if loader_cls.name == meta.loader:
                meta.loader_cls = loader_cls
                break
        else:
            raise ImproperlyConfigured(
                (
                    "Program {} has no loader class registered."
                    "Check PROGRAM_LOADERS or PROGRAM_DIRS"
                ).format(meta.path)
            )


def ac_encode(text, probs):
    """Encode a text using arithmetic coding with the provided probabilities.

    This is a wrapper for :py:meth:`Arithmetic.encode`.

    Parameters
    ----------
    text : str
        A string to encode
    probs : dict
        A probability statistics dictionary generated by
        :py:meth:`Arithmetic.train`

    Returns
    -------
    tuple
        The arithmetically coded text

    Example
    -------
    >>> pr = ac_train('the quick brown fox jumped over the lazy dog')
    >>> ac_encode('align', pr)
    (16720586181, 34)

    """
    coder = Arithmetic()
    coder.set_probs(probs)
    return coder.encode(text)

def ac_encode ( text , probs ) : coder = Arithmetic ( ) coder . set_probs ( probs ) return coder . encode ( text )

Encode a text using arithmetic coding with the provided probabilities .