class NoteDisplay(InstructionGroup): def __init__(self, note_data, planes, ac): super(NoteDisplay, self).__init__() self.note = note_data self.planes = sorted(planes) self.ac = ac self.sound_count = 0 self.past_me = False self.sounds = [] self.sound_group = InstructionGroup() self.color = COLORS[self.note.pitch % 12] self.intensity = self.note.velocity self.texture_indices = [(self.color, i) for i in range(100) +list(reversed(xrange(1,101)))] self.texture_frame = np.random.randint(len(self.texture_indices)) self.billboard = BillboardDisplay(self.pos_from_tick(ac.tick), texture=textures[self.texture_indices[0]], size_x=2.0, size_y=2.0, intensity=self.intensity) self.add(self.sound_group) self.add(self.billboard) def pos_from_tick(self, tick): z = - (self.note.tick - tick) * config['UNITS_PER_TICK'] x = config['LINE_SPACING'] * self.note.x y = config['LINE_SPACING'] * self.note.y return (x, y, z) def set_pos(self, pos): self.billboard.set_pos(pos) def sound(self, tick, pos): # Play Sound self.ac.play_note(tick, self.note, pos) # Render Sound exp_time = max(self.note.duration * 60 / (480 * self.ac.scheduler.cond.bpm), 0.2) sound_display = SoundDisplay(pos, exp_time, self) self.sounds.append(sound_display) self.sound_group.add(sound_display) def on_update(self, dt, tick, angles): self.texture_frame += 1 self.billboard.set_texture(textures[self.texture_indices[int(self.texture_frame) % len(self.texture_indices)]]) self.billboard.set_rotate(angles) for s in self.sounds: if not s.on_update(dt, tick, angles): self.sounds.remove(s) self.sound_group.remove(s)
class DisplayController(object): def __init__(self, width, height, canvas, ac, eye_pos, eye_angle): super(DisplayController, self).__init__() self.width = width self.height = height self.canvas = canvas self.eye_pos = eye_pos self.eye_angle = eye_angle self.ac = ac self.canvas.shader.source = resource_find('data/simple.glsl') self.all_notes = [] self.future_notes = {} self.past_notes = {} self.ticks = [] # self.planes = range(0, 10 * config['PLANE_SPACING'], config['PLANE_SPACING']) self.planes = [] self.lines = [] self.past_displays = InstructionGroup() self.future_displays = InstructionGroup() self.plane_displays = InstructionGroup() self.line_displays = InstructionGroup() self.fixed_x = Translate(0, 0, 0) self.fixed_y = Translate(0, 0, 0) self.fixed_z = Translate(0, 0, 0) self.fixed_azi = Rotate(origin=(0, 0, 0), axis=(0, 1, 0)) self.fixed_ele = Rotate(origin=(0, 0, 0), axis=(1, 0, 0)) self.alpha_callback = Callback(self.alpha_sample_callback) self.disable_alpha_callback = Callback( self.disable_alpha_sample_callback) self.alpha_instruction = InstructionGroup() self.alpha_disable_instruction = InstructionGroup() self.alpha_sample_enable = False self.canvas.add(Callback(self.setup_gl_context)) self.canvas.add(self.alpha_instruction) self.canvas.add(PushMatrix()) self.canvas.add(UpdateNormalMatrix()) self.canvas.add(PushMatrix()) self.canvas.add(self.fixed_z) self.canvas.add(self.line_displays) self.canvas.add(PopMatrix()) self.canvas.add(PushMatrix()) self.canvas.add(self.past_displays) self.canvas.add(self.future_displays) self.canvas.add(PopMatrix()) self.canvas.add(PushMatrix()) # self.canvas.add(self.fixed_x) # self.canvas.add(self.fixed_y) self.canvas.add(self.plane_displays) self.canvas.add(PopMatrix()) # self.canvas.add(PushMatrix()) # self.canvas.add(self.fixed_x) # self.canvas.add(self.fixed_y) # self.canvas.add(self.fixed_z) # self.canvas.add(Plane(-config['SELF_PLANE_DISTANCE'], size=0.1, color=(0x20/255., 0xD8/255., 0xE9/255.), tr=0.2)) # self.canvas.add(PopMatrix()) self.canvas.add(PopMatrix()) self.canvas.add(self.alpha_disable_instruction) self.canvas.add(Callback(self.reset_gl_context)) self.draw_planes() def add_notes(self, note_data): s = config['LINE_SPACING'] for nd in note_data: if (nd.x, nd.y) not in self.lines and float( nd.x).is_integer() and float(nd.y).is_integer(): self.line_displays.add( Line(nd.x * s, nd.y * s, color=(0.7, 0.5, 0.0))) self.lines.append((nd.x, nd.y)) self.all_notes.extend(note_data) self.all_notes.sort(key=lambda n: n.tick) self.ticks = map(lambda n: n.tick, self.all_notes) def draw_planes(self): for p in self.planes: self.plane_displays.add( Plane(p, color=(0xE9 / 255., 0xD8 / 255., 0x3C / 255.))) def setup_gl_context(self, *args): gl.glEnable(gl.GL_DEPTH_TEST) def toggle_alpha_sample(self): if self.alpha_sample_enable: self.alpha_instruction.remove(self.alpha_callback) self.alpha_disable_instruction.remove(self.disable_alpha_callback) self.alpha_sample_enable = False else: self.alpha_instruction.add(self.alpha_callback) self.alpha_disable_instruction.add(self.disable_alpha_callback) self.alpha_sample_enable = True def alpha_sample_callback(self, *args): gl.glEnable(gl.GL_SAMPLE_ALPHA_TO_COVERAGE) def reset_gl_context(self, *args): gl.glDisable(gl.GL_DEPTH_TEST) def disable_alpha_sample_callback(self, *args): gl.glDisable(gl.GL_SAMPLE_ALPHA_TO_COVERAGE) def get_look_at(self, x, y, z, azi, ele): dx = -np.sin(azi) * np.cos(ele) dy = np.sin(ele) dz = -np.cos(azi) * np.cos(ele) # Not sure why up has to just be up... upx, upy, upz = (0, 1, 0) mat = Matrix() mat = mat.look_at(x, y, z, x + dx, y + dy, z + dz, upx, upy, upz) return mat def update_camera(self, pos, angle): self.eye_pos = pos self.eye_angle = angle x, y, z = pos azi, ele = angle asp = self.width / float(self.height) mat = self.get_look_at(x, y, z, azi, ele) proj = Matrix() proj.perspective(30, asp, 1, 100) self.canvas['projection_mat'] = proj self.canvas['modelview_mat'] = mat self.fixed_x.x = x self.fixed_y.y = y self.fixed_z.z = z self.fixed_azi.angle = azi * 180 / np.pi self.fixed_ele.angle = ele * 180 / np.pi def get_notes_in_range(self, start_tick, end_tick): l = bisect.bisect_left(self.ticks, start_tick) r = bisect.bisect_left(self.ticks, end_tick) if r <= 0: return [] return self.all_notes[l:r] def on_update(self, tick): self_plane_z = self.eye_pos[2] - config['SELF_PLANE_DISTANCE'] eye_tick = tick + (-self.eye_pos[2] / config['UNITS_PER_TICK']) dt = kivyClock.frametime future_range = self.get_notes_in_range( eye_tick, eye_tick + config['VISIBLE_TICK_RANGE']) past_range = self.get_notes_in_range( eye_tick - config['VISIBLE_TICK_RANGE'], eye_tick) # COMPLEX LOGIC TO MAINTAIN LISTS OF VISIBLE NOTES ORDERED BY DISTANCE FROM CAMERA # far future <-> future # future <-> past # past <-> far past # far future -> future fftof = list( x for x in future_range if x not in self.past_notes and x not in self.future_notes) # future -> far future ftoff = list(x for x in self.future_notes if x not in future_range and x not in past_range) # future -> past ftop = list(x for x in past_range if x in self.future_notes) # past -> future ptof = list(x for x in future_range if x in self.past_notes) # past -> far past ptofp = list(x for x in self.past_notes if x not in future_range and x not in past_range) # far past -> past fptop = list( x for x in past_range if x not in self.past_notes and x not in self.future_notes) # handle ff -> f for nd in sorted(fftof, key=lambda n: n.tick): ndisp = NoteDisplay(nd, self.planes, self.ac) self.future_displays.insert(0, ndisp) self.future_notes[nd] = ndisp # handle f -> ff for nd in ftoff: ndisp = self.future_notes[nd] self.future_displays.remove(ndisp) del self.future_notes[nd] # handle f -> p for nd in sorted(ftop, key=lambda n: n.tick): ndisp = self.future_notes[nd] self.future_displays.remove(ndisp) self.past_displays.add(ndisp) self.past_notes[nd] = ndisp del self.future_notes[nd] # handle p -> f for nd in sorted(ptof, key=lambda n: -n.tick): ndisp = self.past_notes[nd] self.past_displays.remove(ndisp) self.future_displays.add(ndisp) self.future_notes[nd] = ndisp del self.past_notes[nd] # handle p -> fp for nd in ptofp: ndisp = self.past_notes[nd] self.past_displays.remove(ndisp) del self.past_notes[nd] # handle fp -> p for nd in sorted(fptop, key=lambda n: -n.tick): ndisp = NoteDisplay(nd, self.planes, self.ac) self.past_displays.insert(0, ndisp) self.past_notes[nd] = ndisp for s in self.future_notes.values() + self.past_notes.values(): pos = s.pos_from_tick(tick) s.set_pos(pos) s.on_update(dt, eye_tick, self.eye_angle) if s.past_me and pos[ 2] < self_plane_z - 0.5 * config['SELF_PLANE_DISTANCE']: s.past_me = False if pos[2] > self_plane_z and not s.past_me: s.sound(tick, pos) s.past_me = True Logger.debug('Number of notes: %s' % (len(self.future_notes) + len(self.past_notes)))
class DisplayController(object): def __init__(self, width, height, canvas, ac, eye_pos, eye_angle): super(DisplayController, self).__init__() self.width = width self.height = height self.canvas = canvas self.eye_pos = eye_pos self.eye_angle = eye_angle self.ac = ac self.canvas.shader.source = resource_find('data/simple.glsl') self.all_notes = [] self.future_notes = {} self.past_notes = {} self.ticks = [] # self.planes = range(0, 10 * config['PLANE_SPACING'], config['PLANE_SPACING']) self.planes = [] self.lines = [] self.past_displays = InstructionGroup() self.future_displays = InstructionGroup() self.plane_displays = InstructionGroup() self.line_displays = InstructionGroup() self.fixed_x = Translate(0, 0, 0) self.fixed_y = Translate(0, 0, 0) self.fixed_z = Translate(0, 0, 0) self.fixed_azi = Rotate(origin=(0, 0, 0), axis=(0, 1, 0)) self.fixed_ele = Rotate(origin=(0, 0, 0), axis=(1, 0, 0)) self.alpha_callback = Callback(self.alpha_sample_callback) self.disable_alpha_callback = Callback(self.disable_alpha_sample_callback) self.alpha_instruction = InstructionGroup() self.alpha_disable_instruction = InstructionGroup() self.alpha_sample_enable = False self.canvas.add(Callback(self.setup_gl_context)) self.canvas.add(self.alpha_instruction) self.canvas.add(PushMatrix()) self.canvas.add(UpdateNormalMatrix()) self.canvas.add(PushMatrix()) self.canvas.add(self.fixed_z) self.canvas.add(self.line_displays) self.canvas.add(PopMatrix()) self.canvas.add(PushMatrix()) self.canvas.add(self.past_displays) self.canvas.add(self.future_displays) self.canvas.add(PopMatrix()) self.canvas.add(PushMatrix()) # self.canvas.add(self.fixed_x) # self.canvas.add(self.fixed_y) self.canvas.add(self.plane_displays) self.canvas.add(PopMatrix()) # self.canvas.add(PushMatrix()) # self.canvas.add(self.fixed_x) # self.canvas.add(self.fixed_y) # self.canvas.add(self.fixed_z) # self.canvas.add(Plane(-config['SELF_PLANE_DISTANCE'], size=0.1, color=(0x20/255., 0xD8/255., 0xE9/255.), tr=0.2)) # self.canvas.add(PopMatrix()) self.canvas.add(PopMatrix()) self.canvas.add(self.alpha_disable_instruction) self.canvas.add(Callback(self.reset_gl_context)) self.draw_planes() def add_notes(self, note_data): s = config['LINE_SPACING'] for nd in note_data: if (nd.x, nd.y) not in self.lines and float(nd.x).is_integer() and float(nd.y).is_integer(): self.line_displays.add(Line(nd.x * s, nd.y * s, color=(0.7, 0.5, 0.0))) self.lines.append((nd.x, nd.y)) self.all_notes.extend(note_data) self.all_notes.sort(key=lambda n:n.tick) self.ticks = map(lambda n:n.tick, self.all_notes) def draw_planes(self): for p in self.planes: self.plane_displays.add(Plane(p, color=(0xE9/255., 0xD8/255., 0x3C/255.))) def setup_gl_context(self, *args): gl.glEnable(gl.GL_DEPTH_TEST) def toggle_alpha_sample(self): if self.alpha_sample_enable: self.alpha_instruction.remove(self.alpha_callback) self.alpha_disable_instruction.remove(self.disable_alpha_callback) self.alpha_sample_enable = False else: self.alpha_instruction.add(self.alpha_callback) self.alpha_disable_instruction.add(self.disable_alpha_callback) self.alpha_sample_enable = True def alpha_sample_callback(self, *args): gl.glEnable(gl.GL_SAMPLE_ALPHA_TO_COVERAGE) def reset_gl_context(self, *args): gl.glDisable(gl.GL_DEPTH_TEST) def disable_alpha_sample_callback(self, *args): gl.glDisable(gl.GL_SAMPLE_ALPHA_TO_COVERAGE) def get_look_at(self, x, y, z, azi, ele): dx = - np.sin(azi) * np.cos(ele) dy = np.sin(ele) dz = - np.cos(azi) * np.cos(ele) # Not sure why up has to just be up... upx, upy, upz = (0, 1, 0) mat = Matrix() mat = mat.look_at(x, y, z, x + dx, y + dy, z + dz, upx, upy, upz) return mat def update_camera(self, pos, angle): self.eye_pos = pos self.eye_angle = angle x, y, z = pos azi, ele = angle asp = self.width / float(self.height) mat = self.get_look_at(x, y, z, azi, ele) proj = Matrix() proj.perspective(30, asp, 1, 100) self.canvas['projection_mat'] = proj self.canvas['modelview_mat'] = mat self.fixed_x.x = x self.fixed_y.y = y self.fixed_z.z = z self.fixed_azi.angle = azi * 180/np.pi self.fixed_ele.angle = ele * 180/np.pi def get_notes_in_range(self, start_tick, end_tick): l = bisect.bisect_left(self.ticks, start_tick) r = bisect.bisect_left(self.ticks, end_tick) if r <= 0: return [] return self.all_notes[l:r] def on_update(self, tick): self_plane_z = self.eye_pos[2] - config['SELF_PLANE_DISTANCE'] eye_tick = tick + ( - self.eye_pos[2] / config['UNITS_PER_TICK']) dt = kivyClock.frametime future_range = self.get_notes_in_range(eye_tick, eye_tick + config['VISIBLE_TICK_RANGE']) past_range = self.get_notes_in_range(eye_tick - config['VISIBLE_TICK_RANGE'], eye_tick) # COMPLEX LOGIC TO MAINTAIN LISTS OF VISIBLE NOTES ORDERED BY DISTANCE FROM CAMERA # far future <-> future # future <-> past # past <-> far past # far future -> future fftof = list(x for x in future_range if x not in self.past_notes and x not in self.future_notes) # future -> far future ftoff = list(x for x in self.future_notes if x not in future_range and x not in past_range) # future -> past ftop = list(x for x in past_range if x in self.future_notes) # past -> future ptof = list(x for x in future_range if x in self.past_notes) # past -> far past ptofp = list(x for x in self.past_notes if x not in future_range and x not in past_range) # far past -> past fptop = list(x for x in past_range if x not in self.past_notes and x not in self.future_notes) # handle ff -> f for nd in sorted(fftof, key=lambda n: n.tick): ndisp = NoteDisplay(nd, self.planes, self.ac) self.future_displays.insert(0, ndisp) self.future_notes[nd] = ndisp # handle f -> ff for nd in ftoff: ndisp = self.future_notes[nd] self.future_displays.remove(ndisp) del self.future_notes[nd] # handle f -> p for nd in sorted(ftop, key=lambda n: n.tick): ndisp = self.future_notes[nd] self.future_displays.remove(ndisp) self.past_displays.add(ndisp) self.past_notes[nd] = ndisp del self.future_notes[nd] # handle p -> f for nd in sorted(ptof, key=lambda n: -n.tick): ndisp = self.past_notes[nd] self.past_displays.remove(ndisp) self.future_displays.add(ndisp) self.future_notes[nd] = ndisp del self.past_notes[nd] # handle p -> fp for nd in ptofp: ndisp = self.past_notes[nd] self.past_displays.remove(ndisp) del self.past_notes[nd] # handle fp -> p for nd in sorted(fptop, key=lambda n: -n.tick): ndisp = NoteDisplay(nd, self.planes, self.ac) self.past_displays.insert(0, ndisp) self.past_notes[nd] = ndisp for s in self.future_notes.values() + self.past_notes.values(): pos = s.pos_from_tick(tick) s.set_pos(pos) s.on_update(dt, eye_tick, self.eye_angle) if s.past_me and pos[2] < self_plane_z - 0.5 * config['SELF_PLANE_DISTANCE']: s.past_me = False if pos[2] > self_plane_z and not s.past_me: s.sound(tick, pos) s.past_me = True Logger.debug('Number of notes: %s' % (len(self.future_notes) + len(self.past_notes)))