def render(id: str) -> None: nonlocal error if error is not None: message = error.strip() ig.text('Wafel has crashed. Cause:') lines = message.split('\n') ig.input_text_multiline( '##error-msg', message, len(message) + 1, max(map(len, lines)) * 10, (len(lines) + 1) * ig.get_text_line_height() + 6, ) ig.dummy(10, 10) if ig.button('Exit'): log.info('Aborted') sys.exit(1) ig.same_line() if ig.button('Try to save'): if view.ask_save_filename(): view.save() # ig.same_line() # if view is not None and ig.button('Try to continue (mad lads only)'): # view.reload_ui() # error = None return try: if hasattr(model, 'pipeline'): log.timer.get_num_copies = lambda: model.pipeline.num_copies( ) if config.dev_mode else 0 log.timer.get_num_updates = lambda: model.pipeline.num_advances( ) if config.dev_mode else 0 log.timer.begin_frame() ig.try_render(lambda: do_render(id)) except: error = traceback.format_exc() log.error('Caught: ' + error) finally: log.timer.end_frame()
def cardinal(prefix: str) -> None: ig.dummy(45, 1) ig.same_line() binding_button('n64-' + prefix + '^', prefix + '^') binding_button('n64-' + prefix + '<', prefix + '<') ig.same_line() ig.set_cursor_pos((110, ig.get_cursor_pos().y)) binding_button('n64-' + prefix + '>', prefix + '>') ig.dummy(45, 1) ig.same_line() binding_button('n64-' + prefix + 'v', prefix + 'v')
def render_left_column(self, framebuffer_size: Tuple[int, int]) -> None: total_height = ig.get_window_height() - ig.get_frame_height() # subtract menu bar slider_space = 45 wall_hitbox_radius = use_state('wall-hitbox-radius', 50) wall_hitbox_options = [0, 24, 50, 110] hovered_surface: Ref[Optional[int]] = use_state('hovered-surface', None) new_hovered_surface: Optional[int] = None hidden_surfaces_by_area = \ use_state('hidden-surfaces', cast(Dict[Tuple[int, int], Set[int]], {})).value current_area = ( dcast(int, self.model.get(Variable('level-num').with_frame(self.model.selected_frame))), dcast(int, self.model.get(Variable('area-index').with_frame(self.model.selected_frame))), ) hidden_surfaces = hidden_surfaces_by_area.setdefault(current_area, set()) log.timer.begin('gview1') ig.begin_child( 'Game View 1', height=int(total_height // 2) - slider_space // 2, border=True, ) hovered_surface_1 = ui.render_game_view_rotate( 'game-view-1', framebuffer_size, self.model, wall_hitbox_radius.value, hovered_surface.value, hidden_surfaces, ) ig.set_cursor_pos((10.0, ig.get_window_height() - 30)) ig.text('wall radius') ig.same_line() ig.push_item_width(50) _, index = ig.combo( '##wall-hitbox-radius', wall_hitbox_options.index(wall_hitbox_radius.value), list(map(str, wall_hitbox_options)), ) wall_hitbox_radius.value = wall_hitbox_options[index] ig.pop_item_width() ig.end_child() log.timer.end() log.timer.begin('gview2') ig.begin_child( 'Game View 2', height=int(total_height // 2) - slider_space // 2, border=True, ) hovered_surface_2 = ui.render_game_view_birds_eye( 'game-view-2', framebuffer_size, self.model, wall_hitbox_radius.value, hovered_surface.value, hidden_surfaces, ) ig.end_child() log.timer.end() new_hovered_surface = hovered_surface_1 or hovered_surface_2 if new_hovered_surface is not None and ig.is_mouse_clicked(1): ig.open_popup('surface-ctx') hovered_surface.value = new_hovered_surface if ig.begin_popup('surface-ctx'): if hovered_surface.value is not None: if hovered_surface.value in hidden_surfaces: if ig.menu_item('Show')[0]: hidden_surfaces.remove(hovered_surface.value) else: if ig.menu_item('Hide')[0]: hidden_surfaces.add(hovered_surface.value) if ig.menu_item('Properties')[0]: self.variable_explorer.open_surface_tab(hovered_surface.value) ig.end_popup() else: hovered_surface.value = new_hovered_surface if hovered_surface.value is not None and ig.is_mouse_clicked(2): if hovered_surface.value in hidden_surfaces: hidden_surfaces.remove(hovered_surface.value) else: hidden_surfaces.add(hovered_surface.value) speed_options = [0.05, 0.25, 0.5, 1, 2, 4] saved_play_direction = use_state('saved-play-direction', 0) saved_speed_index = use_state('saved-speed-index', 3) play_direction = saved_play_direction.value speed_index = saved_speed_index.value if play_direction == 0: frame_advance = 0 play_override = 0 def control(name: str, speed: int) -> None: nonlocal frame_advance, play_override x = input_down_gradual(name, 0.25) if x == 1.0: play_override = speed elif input_pressed(name): frame_advance += speed control('frame-next', 1) control('frame-next-alt', 1) control('frame-prev', -1) control('frame-prev-alt', -1) control('frame-next-fast', 10) control('frame-prev-fast', -10) if play_override != 0: if abs(play_override) in speed_options: speed_index = speed_options.index(abs(play_override)) else: speed_index = len(speed_options) - 1 play_direction = 1 if play_override > 0 else -1 else: self.model.selected_frame += frame_advance else: if input_down('frame-next') or input_down('frame-next-alt'): if play_direction == 1: speed_index += 1 else: play_direction = -play_direction elif input_down('frame-prev') or input_down('frame-prev-alt'): if play_direction == -1: speed_index += 1 else: play_direction = -play_direction elif input_down('frame-next-fast'): if play_direction == 1: speed_index += 2 else: play_direction = -play_direction speed_index += 1 elif input_down('frame-prev-fast'): if play_direction == -1: speed_index += 2 else: play_direction = -play_direction speed_index += 1 speed_index = min(max(speed_index, 0), len(speed_options) - 1) self.model.play_speed = play_direction * speed_options[speed_index] self.model.playback_mode = saved_play_direction.value != 0 def play_button(label: str, direction: int) -> None: disabled = play_direction == direction if ig.disableable_button(label, enabled=play_direction != direction): saved_play_direction.value = direction play_button('<|', -1) ig.same_line() play_button('||', 0) ig.same_line() play_button('|>', 1) ig.same_line() ig.push_item_width(63) changed, new_index = ig.combo( '##speed-option', speed_index, [str(s) + 'x' for s in speed_options], ) ig.pop_item_width() if changed: saved_speed_index.value = new_index if input_pressed('playback-play'): if saved_play_direction.value == 0: saved_play_direction.value = 1 else: saved_play_direction.value = 0 if input_pressed('playback-rewind'): if saved_play_direction.value == 0: saved_play_direction.value = -1 else: saved_play_direction.value = 0 if input_pressed('playback-speed-up'): saved_speed_index.value = min(saved_speed_index.value + 1, len(speed_options) - 1) if input_pressed('playback-slow-down'): saved_speed_index.value = max(saved_speed_index.value - 1, 0) ig.same_line() new_frame = ui.render_frame_slider( 'frame-slider', self.model.selected_frame, self.model.max_frame - 1, self.model.pipeline.cached_frames() if self.show_debug_pane else [], ) if new_frame is not None: self.model.selected_frame = new_frame.value
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_input_tab(self, tab: TabId) -> None: column_sizes = [170, 370, 200] ig.set_next_window_content_size(sum(column_sizes), 0) ig.begin_child('##input', flags=ig.WINDOW_HORIZONTAL_SCROLLING_BAR) ig.columns(3) for i, w in enumerate(column_sizes): ig.set_column_width(i, w) def render_button(button: str) -> None: self.render_variable( tab, Variable('input-button-' + button).with_frame( self.model.selected_frame), 10, 25, ) ig.dummy(1, 3) render_button('a') ig.same_line() render_button('b') ig.same_line() render_button('z') ig.dummy(1, 5) render_button('s') ig.same_line() ig.dummy(43, 1) ig.same_line() render_button('r') ig.dummy(1, 5) ig.dummy(43, 1) ig.same_line() render_button('cu') ig.dummy(17, 1) ig.same_line() render_button('cl') ig.same_line() render_button('cr') ig.dummy(43, 1) ig.same_line() render_button('cd') ig.next_column() self.render_intended_stick_control('intended') ig.next_column() self.render_stick_control('joystick', tab) ig.columns(1) ig.end_child()
def render_intended_stick_control(self, id: str) -> None: up_options = ['3d view', 'mario yaw', 'stick y', 'world x'] up_option = use_state('up-option', 0) ig.text('up =') ig.same_line() ig.push_item_width(100) _, up_option.value = ig.combo('##up-option', up_option.value, up_options) ig.pop_item_width() ig.dummy(1, 10) 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) face_yaw = dcast( int, self.model.get( Variable('mario-face-yaw').with_frame( self.model.selected_frame))) camera_yaw = dcast( int, self.model.get( Variable('camera-yaw').with_frame(self.model.selected_frame)) or 0) squish_timer = dcast( int, self.model.get(self.model.selected_frame, 'gMarioState->squishTimer')) active_face_yaw = face_yaw events = self.model.pipeline.frame_log(self.model.selected_frame + 1) active_face_yaw_action = None for event in events: if event['type'] == 'FLT_EXECUTE_ACTION': action_name = self.model.action_names[event['action']] active_face_yaw = event['faceAngle'][1] active_face_yaw_action = action_name if action_name == 'idle': break up_angle = { 'mario yaw': active_face_yaw, 'stick y': camera_yaw + 0x8000, 'world x': 0x4000, '3d view': self.model.rotational_camera_yaw, }[up_options[up_option.value]] self.model.input_up_yaw = up_angle raw_stick_x = dcast(int, self.model.get(stick_x_var)) raw_stick_y = dcast(int, self.model.get(stick_y_var)) adjusted = stick_raw_to_adjusted(raw_stick_x, raw_stick_y) intended = stick_adjusted_to_intended( adjusted, face_yaw, camera_yaw, squish_timer != 0, ) def render_value(label: str, value: object, formatter: VariableFormatter) -> Optional[Any]: label_width = 60 value_size = ( 60 if label == 'dyaw' else 80, ig.get_text_line_height() + 2 * ig.get_style().frame_padding[1], ) ig.push_item_width(label_width) ig.selectable(label, width=label_width) ig.pop_item_width() ig.same_line() new_value, _, _ = ui.render_variable_value('value-' + label, value, formatter, value_size) return None if new_value is None else new_value.value target_yaw: Optional[int] = None target_dyaw: Optional[int] = None target_mag: Optional[float] = None target_mag = render_value('int mag', intended.mag, FloatFormatter()) target_yaw = render_value('int yaw', intended.yaw, DecimalIntFormatter()) dyaw = intended.yaw - active_face_yaw target_dyaw = render_value('dyaw', dyaw, DecimalIntFormatter()) ig.same_line() if ig.button('?'): ig.open_popup('active-yaw-expl') if ig.begin_popup('active-yaw-expl'): ig.text(f'{intended.yaw} - {active_face_yaw} = {dyaw}') ig.text(f'intended yaw = {intended.yaw}') if active_face_yaw == face_yaw: ig.text(f'face yaw = {face_yaw}') if active_face_yaw != face_yaw: ig.text( f'face yaw = {active_face_yaw} at start of {active_face_yaw_action} action' ) ig.text(f'(face yaw = {face_yaw} at start of frame)') ig.end_popup() if dyaw not in range(0, 16): if ig.button('dyaw = 0'): target_dyaw = 0 if target_yaw is not None or target_dyaw is not None or target_mag is not None: relative_to = 0 if target_yaw is not None else active_face_yaw if target_dyaw is not None: target_yaw = active_face_yaw + target_dyaw if target_yaw is None: target_yaw = intended.yaw if target_mag is None: target_mag = intended.mag new_raw_stick_x, new_raw_stick_y = intended_to_raw( face_yaw, camera_yaw, squish_timer, target_yaw, target_mag, relative_to) self.model.set(stick_x_var, new_raw_stick_x) self.model.set(stick_y_var, new_raw_stick_y) n_a = intended.yaw - up_angle n_x = intended.mag / 32 * math.sin(-n_a * math.pi / 0x8000) n_y = intended.mag / 32 * math.cos(n_a * math.pi / 0x8000) ig.set_cursor_pos((ig.get_cursor_pos().x + 155, 0)) new_n = ui.render_joystick_control(id, n_x, n_y, 'circle') if new_n is not None: new_n_a = int(math.atan2(-new_n[0], new_n[1]) * 0x8000 / math.pi) new_intended_yaw = up_angle + new_n_a new_intended_mag = 32 * math.sqrt(new_n[0]**2 + new_n[1]**2) new_raw_stick_x, new_raw_stick_y = intended_to_raw( face_yaw, camera_yaw, squish_timer, new_intended_yaw, new_intended_mag, relative_to=0) self.model.set(stick_x_var, new_raw_stick_x) self.model.set(stick_y_var, new_raw_stick_y)