def do_render(id: str) -> None: nonlocal view if view is None: view = View(model) ig.push_id(id) log.timer.begin('render') view.render() log.timer.end() ig.pop_id() last_fps_time = use_state_with('last-fps-time', lambda: time.time()) frame_count = use_state('frame-count', 0) fps = use_state('fps', 0.0) if hasattr(model, 'pipeline'): frame_count.value += 1 if time.time() > last_fps_time.value + 5: fps.value = frame_count.value / (time.time() - last_fps_time.value) last_fps_time.value = time.time() frame_count.value = 0 log.info( f'mspf: {int(1000 / fps.value * 10) / 10} ({int(fps.value)} fps)' f' - cache={model.pipeline.data_cache_size() // 1024}KB' ) log.timer.begin('balance') model.pipeline.balance_distribution(1/120) log.timer.end()
def render_right_column(self) -> None: total_height = ig.get_window_height() if self.show_debug_pane: ig.push_id('debug-pane') ig.begin_child('##pane', height=int(ig.get_window_height() * 0.15)) ig.columns(2) ig.set_column_width(-1, ig.get_window_width() - 300) ig.begin_child('##log') def init_log() -> List[str]: messages = [] log.subscribe(lambda msg: messages.append(str(msg))) return messages messages = use_state_with('messages', init_log).value prev_length = use_state('prev-length', 0) total_height -= ig.get_window_height() if prev_length.value != len(messages): prev_length.value = len(messages) ig.set_scroll_y(ig.get_scroll_max_y() + ig.get_window_height()) for message in messages: ig.text(message) ig.end_child() ig.next_column() for line in log.timer.format(log.timer.get_summaries()): ig.text(line) ig.columns(1) ig.end_child() ig.pop_id() log.timer.begin('fsheet') frame_sheet = self.frame_sheets[0] ig.set_next_window_content_size(frame_sheet.get_content_width(), 0) ig.begin_child( 'Frame Sheet##' + str(epoch) + '-0', height=int(total_height * 0.7), flags=ig.WINDOW_HORIZONTAL_SCROLLING_BAR, ) frame_sheet.render() ig.end_child() if ig.begin_drag_drop_target(): payload = ig.accept_drag_drop_payload('ve-var') if payload is not None: frame_sheet.append_variable(Variable.from_bytes(payload)) ig.end_drag_drop_target() log.timer.end() log.timer.begin('varexp') ig.begin_child('Variable Explorer', border=True) self.variable_explorer.render('variable-explorer') ig.end_child() log.timer.end()
def render(self) -> None: ig.push_id(str(epoch)) # if ig.is_key_pressed(ord('`')): # self.show_debug_pane = not self.show_debug_pane self.handle_controller() prev_frame_time = use_state_with('prev-frame-time', time.time) accum_time = use_state('accum-time', 0.0) now = time.time() accum_time.value += now - prev_frame_time.value prev_frame_time.value = now play_speed = self.model.play_speed if play_speed == 0.0: accum_time.value = 0 else: target_fps = 30 * abs(play_speed) target_dt = 1 / target_fps updates = 0 while accum_time.value >= target_dt and updates < 20: accum_time.value -= target_dt self.model.selected_frame += 1 if play_speed > 0 else -1 self.handle_controller() updates += 1 ig_window_size = ig.get_window_size() window_size = (int(ig_window_size.x), int(ig_window_size.y)) ig.columns(2) self.render_left_column(window_size) ig.next_column() self.render_right_column() ig.columns(1) ig.pop_id()
def handle_controller(self) -> None: ig.push_id('controller-inputs') buttons_enabled = use_state('buttons-enabled', False) stick_enabled = use_state('stick-enabled', False) def add_callbacks() -> Ref[bool]: input_edit = Ref(False) def disable_controller(*args, **kwargs) -> None: if not input_edit.value: buttons_enabled.value = False stick_enabled.value = False self.model.on_edit(disable_controller) def frame_change(*args, **kwargs) -> None: if self.model.play_speed == 0.0: disable_controller() self.model.on_selected_frame_change(frame_change) return input_edit input_edit = use_state_with('initialize', add_callbacks).value prev_play_speed = use_state('prev-play-speed', 0.0) if self.model.play_speed != prev_play_speed.value: buttons_enabled.value = False stick_enabled.value = False prev_play_speed.value = self.model.play_speed controller_button_values = { 'input-button-a': input_down('n64-A'), 'input-button-b': input_down('n64-B'), 'input-button-z': input_down('n64-Z'), 'input-button-s': input_down('n64-S'), 'input-button-l': input_down('n64-L'), 'input-button-r': input_down('n64-R'), 'input-button-cu': input_down('n64-C^'), 'input-button-cl': input_down('n64-C<'), 'input-button-cr': input_down('n64-C>'), 'input-button-cd': input_down('n64-Cv'), 'input-button-du': input_down('n64-D^'), 'input-button-dl': input_down('n64-D<'), 'input-button-dr': input_down('n64-D>'), 'input-button-dd': input_down('n64-Dv'), } if any(controller_button_values.values()): buttons_enabled.value = True stick_enabled.value = True for variable_name, new_button_value in controller_button_values.items(): variable = Variable(variable_name).with_frame(self.model.selected_frame) button_value = self.model.get(variable) if buttons_enabled.value and button_value != new_button_value: input_edit.value = True self.model.set(variable, new_button_value) input_edit.value = False controller_stick_values = ( input_float('n64->') - input_float('n64-<'), input_float('n64-^') - input_float('n64-v'), ) # Require a larger magnitude for enabling controller since dead zone may be too small if any(abs(v) > 0.1 for v in controller_stick_values): stick_enabled.value = True buttons_enabled.value = True if stick_enabled.value: stick_x_var = Variable('input-stick-x').with_frame(self.model.selected_frame) stick_y_var = Variable('input-stick-y').with_frame(self.model.selected_frame) new_stick = self.compute_stick_from_controller(*controller_stick_values) stick = (self.model.get(stick_x_var), self.model.get(stick_y_var)) if stick != new_stick: input_edit.value = True self.model.set(stick_x_var, new_stick[0]) self.model.set(stick_y_var, new_stick[1]) input_edit.value = False ig.pop_id()
def use_rotational_camera( framebuffer_size: Tuple[int, int], model: Model, ) -> Tuple[core.RotateCamera, bool]: mouse_state = use_state('mouse-state', MouseTracker()).value target: Ref[Optional[Vec3f]] = use_state('target', None) target_vel: Ref[Optional[Vec3f]] = use_state('target-vel', None) pitch = use_state('pitch', 0.0) yaw = use_state('yaw', 0.0) zoom = use_state('zoom', 0.0) prev_frame_time = use_state_with('prev-frame-time', time.time) lock_to_in_game = use_state('lock-to-in-game', False) delta_time = time.time() - prev_frame_time.value prev_frame_time.value = time.time() drag_amount = mouse_state.get_drag_amount() pitch.value -= drag_amount[1] / 200 yaw.value -= drag_amount[0] / 200 wheel_amount = mouse_state.get_wheel_amount() zoom.value += wheel_amount / 5 zoom.value = min(zoom.value, 7.0) mario_pos = get_mario_pos(model) target_pos = mario_pos if target.value is None else target.value fov_y = math.radians(45) if drag_amount != (0.0, 0.0) or wheel_amount != 0.0: lock_to_in_game.value = False if lock_to_in_game.value: target_pos = cast( Vec3f, model.get(model.selected_frame, 'gLakituState.focus')) target.value = target_pos camera_pos = cast(Vec3f, model.get(model.selected_frame, 'gLakituState.pos')) dpos = ( target_pos[0] - camera_pos[0], target_pos[1] - camera_pos[1], target_pos[2] - camera_pos[2], ) pitch.value, yaw.value = direction_to_angle(dpos) offset = math.sqrt(sum(c**2 for c in dpos)) if offset > 0.001: zoom.value = math.log(offset / 1500, 0.5) fov_y = math.radians( cast(float, model.get(model.selected_frame, 'sFOVState.fov'))) offset = 1500 * math.pow(0.5, zoom.value) face_direction = angle_to_direction(pitch.value, yaw.value) move = [0.0, 0.0, 0.0] # forward, up, right move[0] += input_float('3d-camera-move-f') move[0] -= input_float('3d-camera-move-b') move[1] += input_float('3d-camera-move-u') move[1] -= input_float('3d-camera-move-d') move[2] += input_float('3d-camera-move-r') move[2] -= input_float('3d-camera-move-l') if move != [0.0, 0.0, 0.0] or (target.value is not None and not lock_to_in_game.value): mag = math.sqrt(sum(c**2 for c in move)) if mag > 1: move = [c / mag for c in move] max_speed = 50.0 * delta_time * math.sqrt(offset) f = (math.sin(yaw.value), 0, math.cos(yaw.value)) u = (0, 1, 0) r = (-f[2], 0, f[0]) end_vel = cast( Vec3f, tuple(max_speed * move[0] * f[i] + max_speed * move[1] * u[i] + max_speed * move[2] * r[i] for i in range(3))) accel = 10.0 * delta_time * math.sqrt(offset) current_vel = target_vel.value or (0.0, 0.0, 0.0) target_vel.value = move_toward(current_vel, end_vel, accel) target.value = ( target_pos[0] + target_vel.value[0], target_pos[1] + target_vel.value[1], target_pos[2] + target_vel.value[2], ) target_pos = target.value lock_to_in_game.value = False if ig.disableable_button('Lock to Mario', enabled=target.value is not None): target.value = None target_vel.value = None lock_to_in_game.value = False ig.same_line() if ig.disableable_button('Lakitu', enabled=not lock_to_in_game.value): lock_to_in_game.value = True camera_pos = ( target_pos[0] - offset * face_direction[0], target_pos[1] - offset * face_direction[1], target_pos[2] - offset * face_direction[2], ) camera = core.RotateCamera() camera.pos = camera_pos camera.target = target_pos camera.fov_y = fov_y show_camera_target = target.value is not None and not lock_to_in_game.value return camera, show_camera_target
def render_tabs( id: str, tabs: List[TabInfo], open_tab_index: Optional[int] = None, allow_windowing=False, ) -> Tuple[Optional[int], Optional[int]]: ig.push_id(id) root_id = get_local_state_id_stack() ig.columns(2) closed_tab = None rendered = use_state('rendered', False) if not rendered.value: rendered.value = True ig.set_column_width(-1, 120) if len(tabs) == 0: ig.pop_id() return None, closed_tab selected_tab_index = use_state_with('selected-tab-index', lambda: open_tab_index or 0) selected_tab_id = use_state_with('selected-tab', lambda: tabs[selected_tab_index.value].id) if open_tab_index is not None: selected_tab_index.value = open_tab_index selected_tab_id.value = tabs[open_tab_index].id windowed_tabs = use_state('windowed-tabs', cast(Set[str], set())).value # TODO: Change selected tab if windowed # Handle deletion/insertion if selected_tab_index.value >= len(tabs): selected_tab_index.value = len(tabs) - 1 if tabs[selected_tab_index.value].id != selected_tab_id.value: matching_indices = [ i for i in range(len(tabs)) if tabs[i].id == selected_tab_id.value ] if len(matching_indices) > 0: selected_tab_index.value = matching_indices[0] else: selected_tab_id.value = tabs[selected_tab_index.value].id ig.begin_child('tabs') for i, tab in enumerate(tabs): if tab.id in windowed_tabs: continue _, selected = ig.selectable( tab.label + '##tab-' + tab.id, selected_tab_id.value == tab.id, ) if selected: selected_tab_index.value = i selected_tab_id.value = tab.id if tab.closable and ig.is_item_hovered() and ig.is_mouse_clicked(2): closed_tab = i if allow_windowing or tab.closable: if ig.begin_popup_context_item(f'##ctx-{tab.id}'): if allow_windowing and ig.selectable('Pop out')[0]: windowed_tabs.add(tab.id) if tab.closable and ig.selectable('Close')[0]: closed_tab = i ig.end_popup_context_item() ig.end_child() ig.next_column() ig.begin_child('content', flags=ig.WINDOW_HORIZONTAL_SCROLLING_BAR) tab = tabs[selected_tab_index.value] if tab.id not in windowed_tabs: push_local_state_rebase(('rebase-tabs', ) + root_id) tab.render(tab.id) # type: ignore pop_local_state_rebase() ig.end_child() ig.columns(1) for tab_id in set(windowed_tabs): matching = [tab for tab in tabs if tab.id == tab_id] if len(matching) == 0: windowed_tabs.remove(tab.id) continue tab = matching[0] ig.set_next_window_size(*ig.get_window_size(), ig.ONCE) ig.set_next_window_position(*ig.get_window_position(), ig.ONCE) ig.push_style_color(ig.COLOR_WINDOW_BACKGROUND, 0.06, 0.06, 0.06, 0.94) _, opened = ig.begin( tab.label + '##window-' + tab.id, closable=True, flags=ig.WINDOW_HORIZONTAL_SCROLLING_BAR, ) push_local_state_rebase(('rebase-tabs', ) + root_id) tab.render(tab.id) # type: ignore pop_local_state_rebase() ig.end() ig.pop_style_color() if not opened: windowed_tabs.remove(tab.id) ig.pop_id() return ( None if open_tab_index == selected_tab_index.value else selected_tab_index.value, closed_tab, )