def run_operation( self, operation: Callable[[], OperationReturnType], title="Amulet", msg="Running Operation", throw_exceptions=False, ) -> Any: def operation_wrapper(): yield 0, "Disabling Threads" self.renderer.disable_threads() yield 0, msg op = operation() if isinstance(op, GeneratorType): yield from op yield 0, "Creating Undo Point" yield from self.create_undo_point_iter() return op err = None out = None try: out = show_loading_dialog( operation_wrapper, title, msg, self, ) except OperationError as e: msg = f"Error running operation: {e}" log.info(msg) self.world.restore_last_undo_point() wx.MessageDialog(self, msg, style=wx.OK).ShowModal() err = e except OperationSuccessful as e: msg = str(e) log.info(msg) self.world.restore_last_undo_point() wx.MessageDialog(self, msg, style=wx.OK).ShowModal() err = e except OperationSilentAbort as e: self.world.restore_last_undo_point() err = e except Exception as e: log.error(traceback.format_exc()) dialog = TracebackDialog( self, "Exception while running operation", str(e), traceback.format_exc(), ) dialog.ShowModal() dialog.Destroy() err = e self.world.restore_last_undo_point() self.renderer.enable_threads() self.renderer.render_world.rebuild_changed() if err is not None and throw_exceptions: raise err return out
def create_geometry(self): try: chunk = self.chunk except ChunkDoesNotExist: self._create_empty_geometry() self._chunk_state = 0 except ChunkLoadError: log.info(f"Error loading chunk {self.coords}", exc_info=True) self._create_error_geometry() self._chunk_state = 1 else: self._changed_time = chunk.changed_time self._chunk_state = 2 chunk_verts, chunk_verts_translucent = self._create_lod0_multi( self._sub_chunks(chunk.blocks)) self._set_verts(chunk_verts, chunk_verts_translucent) if self._draw_floor or self._draw_ceil: plane = self._create_grid( "amulet", "amulet_ui/translucent_white", (0.55, 0.5, 0.9) if (self.cx + self.cz) % 2 else (0.4, 0.4, 0.85), ) self.verts = numpy.concatenate([self.verts, plane.ravel()], 0) self.draw_count += len(plane) self._needs_rebuild = True
def _create_atlas(self): texture_atlas, self._texture_bounds, width, height = textureatlas.create_atlas( self._resource_pack.textures) glBindTexture(GL_TEXTURE_2D, self._gl_texture_atlas) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture_atlas) log.info('Finished setting up texture atlas in OpenGL')
def _setup_texture(self, context_id: str): """Set up the texture for a given context""" gl_texture = self._gl_textures[context_id] = glGenTextures( 1 ) # Create the texture location glBindTexture(GL_TEXTURE_2D, gl_texture) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) glBindTexture(GL_TEXTURE_2D, gl_texture) glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, self._image_width, self._image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, self._image, ) glBindTexture(GL_TEXTURE_2D, 0) log.info("Finished setting up texture atlas in OpenGL")
def reload_operations(self): """Reload all operations and repopulate the UI.""" # store the id of the old operation operation_id = self.active_operation_id # reload the operations self._operations.reload() # repopulate the selection self._operation_choice.SetItems( {op.identifier: op.name for op in self._operations.operations} ) if operation_id: identifiers = self._operation_choice.values if identifiers: if operation_id in identifiers: self._operation_choice.SetSelection(identifiers.index(operation_id)) else: log.info(f"Operation {operation_id} was not found.") self._operation_choice.SetSelection(0) else: log.error("No operations found. Something has gone wrong.") self._setup_operation() self.canvas.reset_bound_events()
def __init__(self, parent: wx.Window, world_dirs, open_world_callback, sort=True): super().__init__(parent) sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(sizer) self.worlds = [] world_formats = [] for world_path in world_dirs: if os.path.isdir(world_path): try: world_formats.append(load_format(world_path)) except FormatError as e: log.info(f"Could not find loader for {world_path} {e}") except Exception: log.error( f"Error loading format wrapper for {world_path} {traceback.format_exc()}" ) if sort: world_formats = reversed(sorted(world_formats, key=lambda f: f.last_played)) for world_format in world_formats: try: world_button = WorldUIButton(self, world_format, open_world_callback) sizer.Add( world_button, 0, wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, 5 ) self.worlds.append(world_button) except Exception as e: log.info(f"Failed to display world button for {world_format.path} {e}") self.Layout()
def create_geometry(self): try: chunk = self.chunk except ChunkDoesNotExist: self._create_empty_geometry() self._chunk_state = 0 except ChunkLoadError: log.info(f"Error loading chunk {self.coords}", exc_info=True) self._create_error_geometry() self._chunk_state = 1 else: self._changed_time = chunk.changed_time self._chunk_state = 2 chunk_verts, chunk_verts_translucent = self._create_lod0_multi( self._sub_chunks(chunk.blocks)) self._set_verts(chunk_verts, chunk_verts_translucent) if self._draw_floor: plane: numpy.ndarray = numpy.ones((self._vert_len * 12), dtype=numpy.float32).reshape( (-1, self._vert_len)) plane[:, :3], plane[:, 3:5] = self._create_chunk_plane(-0.01) plane[:, 5:9] = self.resource_pack.texture_bounds( self.resource_pack.get_texture_path( "amulet", "amulet_ui/translucent_white")) if (self.cx + self.cz) % 2: plane[:, 9:12] = [0.55, 0.5, 0.9] else: plane[:, 9:12] = [0.4, 0.4, 0.85] self.verts = numpy.concatenate([self.verts, plane.ravel()], 0) self.draw_count += 12 self._rebuild = True
def _check_close_world(self) -> bool: """ Check if it is safe to close the world and prompt the user if it is not. :return: True if the world can be closed, False otherwise """ unsaved_changes = self._world.history_manager.unsaved_changes if unsaved_changes: msg = wx.MessageDialog( self, f"""There { 'is' if unsaved_changes == 1 else 'are' } {unsaved_changes} unsaved change{ 's' if unsaved_changes >= 2 else '' } in { self._world.level_wrapper.level_name }. Would you like to save?""", style=wx.YES_NO | wx.CANCEL | wx.CANCEL_DEFAULT, ) response = msg.ShowModal() if response == wx.ID_YES: self._canvas.save() return True elif response == wx.ID_NO: return True elif response == wx.ID_CANCEL: log.info(f"""Aborting closing world { self._world.level_wrapper.level_name } because the user pressed cancel.""") return False return True
def __init__(self, parent, world_dirs, open_world_callback, sort=True): super(WorldList, self).__init__(parent) self.worlds = [] world_formats = [] for world_path in world_dirs: if os.path.isdir(world_path): try: world_formats.append( world_interface.load_format(world_path)) except Exception as e: log.info(f"Could not find loader for {world_path} {e}") if sort: world_formats = reversed( sorted(world_formats, key=lambda f: f.last_played)) for world_format in world_formats: try: world_button = WorldUIButton(self, world_format, open_world_callback) self.add_object(world_button, 0, wx.ALL | wx.EXPAND) self.worlds.append(world_button) except Exception as e: log.info( f"Failed to display world button for {world_format.world_path} {e}" ) self.Layout()
def is_closeable(self) -> bool: """ Check if it is safe to close the UI. :return: True if the program can be closed, False otherwise """ if self._canvas is not None: if self._canvas.is_closeable(): return self._check_close_world() log.info(f"The canvas in edit for world {self._world.world_wrapper.world_name} was not closeable for some reason.") return False return not bool(self._world.chunk_history_manager.unsaved_changes)
def _on_close_app(self, evt): close = True for path, page in list(self._open_worlds.items()): page: CLOSEABLE_PAGE_TYPE if page.is_closeable(): self.close_world(path) else: log.info(f"{page.world_name} cannot be closed.") close = False if close: evt.Skip() else: wx.MessageBox('A world is still being used. Please close it first')
def run_operation(self, operation: Callable[[], OperationReturnType], title="", msg="", throw_exceptions=False) -> Any: self._disable_threads() err = None out = None try: out = show_loading_dialog( operation, title, msg, self, ) self.world.create_undo_point() wx.PostEvent(self, CreateUndoEvent()) except OperationError as e: msg = f"Error running operation: {e}" log.info(msg) self.world.restore_last_undo_point() wx.MessageDialog(self, msg, style=wx.OK).ShowModal() err = e except OperationSuccessful as e: msg = str(e) log.info(msg) self.world.restore_last_undo_point() wx.MessageDialog(self, msg, style=wx.OK).ShowModal() err = e except OperationSilentAbort as e: self.world.restore_last_undo_point() err = e except Exception as e: self.world.restore_last_undo_point() log.error(traceback.format_exc()) wx.MessageDialog( self, f"Exception running operation: {e}\nSee the console for more details", style=wx.OK).ShowModal() err = e self._enable_threads() if err is not None and throw_exceptions: raise err return out
def _convert_method(self): global work_count try: out_world = world_interface.load_format(self.out_world_path) log.info(f'Converting world {self.world.world_path} to {out_world.world_path}') out_world: WorldFormatWrapper out_world.open() self.world.save(out_world, self._update_loading_bar) out_world.close() message = 'World conversion completed' log.info(f'Finished converting world {self.world.world_path} to {out_world.world_path}') except Exception as e: message = f'Error during conversion\n{e}' log.error(message, exc_info=True) self._update_loading_bar(0, 100) self.convert_button.Enable() wx.MessageBox( message ) work_count -= 1
def _convert_method(self): global work_count try: out_world = load_format(self.out_world_path) log.info( f"Converting world {self.world.level_path} to {out_world.path}" ) out_world: WorldFormatWrapper out_world.open() self.world.save(out_world, self._update_loading_bar) out_world.close() message = lang.get("program_convert.conversion_completed") log.info( f"Finished converting world {self.world.level_path} to {out_world.path}" ) except Exception as e: message = f"Error during conversion\n{e}" log.error(message, exc_info=True) self._update_loading_bar(0, 100) self.convert_button.Enable() wx.MessageBox(message) work_count -= 1
def create_atlas_iter( texture_tuple: Tuple[str, ...] ) -> Generator[float, None, Tuple[numpy.ndarray, Dict[Any, Tuple[ float, float, float, float]], int, int], ]: log.info("Creating texture atlas") # Parse texture names textures = [] for texture_index, texture in enumerate(texture_tuple): if not texture_index % 100: yield texture_index / (len(texture_tuple) * 2) # Look for a texture name name, frames = texture, [texture] # Build frame objects frames = [Frame(f) for f in frames] # Add frames to texture object list textures.append(Texture(name, frames)) # Sort textures by perimeter size in non-increasing order textures = sorted(textures, key=lambda i: i.frames[0].perimeter, reverse=True) height = 0 width = 0 pixels = 0 for t in textures: for f in t.frames: height = max(f.height, height) width = max(f.width, width) pixels += f.height * f.width size = max(height, width, 1 << (math.ceil(pixels**0.5) - 1).bit_length()) atlas_created = False atlas = None while not atlas_created: try: # Create the atlas and pack textures in log.info( f"Trying to pack textures into image of size {size}x{size}") atlas = TextureAtlas(size, size) for texture_index, texture in enumerate(textures): if not texture_index % 30: yield 0.5 + texture_index / (len(textures) / 2) atlas.pack(texture) atlas_created = True except AtlasTooSmall: log.info(f"Image was too small. Trying with a larger area") size *= 2 log.info( f"Successfully packed textures into an image of size {size}x{size}") texture_atlas = numpy.array(atlas.generate("RGBA"), numpy.uint8).ravel() texture_bounds = atlas.to_dict() texture_bounds = { texture_path: texture_bounds[texture_path] for texture_path in texture_tuple } log.info("Finished creating texture atlas") return texture_atlas, texture_bounds, atlas.width, atlas.height
def is_closeable(self): if work_count: log.info(f'World {self.world.world_path} is still being converted. Please let it finish before closing') return work_count == 0
def create_atlas( texture_dict: Dict[Any, str] ) -> Tuple[numpy.ndarray, Dict[Any, Tuple[float, float, float, float]], int, int]: log.info('Creating texture atlas') # Parse texture names textures = [] for texture in texture_dict.values(): # Look for a texture name name, frames = texture, [texture] # Build frame objects frames = [Frame(f) for f in frames] # Add frames to texture object list textures.append(Texture(name, frames)) # Sort textures by perimeter size in non-increasing order textures = sorted(textures, key=lambda i: i.frames[0].perimeter, reverse=True) height = 0 width = 0 pixels = 0 for t in textures: for f in t.frames: height = max(f.height, height) width = max(f.width, width) pixels += f.height * f.width size = max(height, width, 1 << (math.ceil(pixels**0.5) - 1).bit_length()) atlas_created = False atlas = None while not atlas_created: try: # Create the atlas and pack textures in log.info( f'Trying to pack textures into image of size {size}x{size}') atlas = TextureAtlas(size, size) for texture in textures: atlas.pack(texture) atlas_created = True except AtlasTooSmall: log.info(f'Image was too small. Trying with a larger area') size *= 2 log.info( f'Successfully packed textures into an image of size {size}x{size}') texture_atlas = numpy.array(atlas.generate('RGBA'), numpy.uint8).ravel() texture_bounds = atlas.to_dict() texture_bounds = { tex_id: texture_bounds[texture_path] for tex_id, texture_path in texture_dict.items() } log.info('Finished creating texture atlas') return texture_atlas, texture_bounds, atlas.width, atlas.height