# (at your option) any later version. # pyspades is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with pyspades. If not, see <http://www.gnu.org/licenses/>. from pyspades.vxl import VXLData map = VXLData() for x in xrange(50, 512 - 50): for y in xrange(50, 512 - 50): for z in xrange(20, 58): map.set_point(x, y, z, (20, 20, 30, 40)) for x in range(512): for y in xrange(512): map.set_point(x, y, 62, (20, 20, 30, 40)) map.set_point(x, y, 63, (20, 20, 30, 40)) for z in range(5, 64): map.set_point(256, 256, z, (20, 20, 30, 40)) print 'generating' open('worldstick.vxl', 'wb').write(map.generate())
class MapEditor(QtGui.QMainWindow): path = None filename = None voxed_filename = None layers = None dirty = False def __init__(self, app, *arg, **kw): super(MapEditor, self).__init__(*arg, **kw) self.app = app self.app.setApplicationName('pyspades map editor') self.setWindowTitle('pyspades map editor') self.clipboard = app.clipboard() menu = self.menuBar() self.file = menu.addMenu('&File') self.new_action = QtGui.QAction('&New', self, shortcut = QtGui.QKeySequence('Ctrl+N'), triggered = self.new_selected) self.file.addAction(self.new_action) self.open_action = QtGui.QAction('&Open...', self, shortcut = QtGui.QKeySequence('Ctrl+O'), triggered = self.open_selected) self.file.addAction(self.open_action) self.file.addSeparator() self.save_action = QtGui.QAction('&Save', self, shortcut = QtGui.QKeySequence.Save, triggered = self.save_selected) self.save_action.setEnabled(False) self.file.addAction(self.save_action) self.save_as_action = QtGui.QAction('Save &As...', self, shortcut = QtGui.QKeySequence('Ctrl+Shift+S'), triggered = self.save_as_selected) self.file.addAction(self.save_as_action) self.file.addSeparator() self.import_menu = self.file.addMenu('&Import') self.import_image_sequence_menu = QtGui.QAction('Image &sequence...', self, triggered = self.import_image_sequence) self.import_menu.addAction(self.import_image_sequence_menu) self.export = self.file.addMenu('&Export') self.color_map = QtGui.QAction('&Colormap...', self, triggered = self.export_color_map) self.export.addAction(self.color_map) self.height_map = QtGui.QAction('&Heightmap...', self, triggered = self.export_height_map) self.export.addAction(self.height_map) self.export_image_sequence_menu = QtGui.QAction('Image &sequence...', self, triggered = self.export_image_sequence) self.export.addAction(self.export_image_sequence_menu) self.file.addSeparator() self.voxed_action = QtGui.QAction('Open in &VOXED', self, shortcut = QtGui.QKeySequence('F5'), triggered = self.open_voxed) self.file.addAction(self.voxed_action) self.file.addSeparator() self.quit_action = QtGui.QAction('E&xit', self, shortcut = QtGui.QKeySequence('Ctrl+Q'), triggered = self.quit) self.file.addAction(self.quit_action) self.edit = menu.addMenu('&Edit') self.copy_action = QtGui.QAction('C&opy', self, shortcut = QtGui.QKeySequence.Copy, triggered = self.copy_selected) self.edit.addAction(self.copy_action) self.paste_action = QtGui.QAction('P&aste', self, shortcut = QtGui.QKeySequence.Paste, triggered = self.paste_selected) self.edit.addAction(self.paste_action) self.copy_action_external = QtGui.QAction('&Copy External', self, shortcut = QtGui.QKeySequence('Ctrl+Shift+C'), triggered = self.copy_external_selected) self.edit.addAction(self.copy_action_external) self.paste_action_external = QtGui.QAction('&Paste External', self, shortcut = QtGui.QKeySequence('Ctrl+Shift+V'), triggered = self.paste_external_selected) self.edit.addAction(self.paste_action_external) self.clear_action = QtGui.QAction('Cl&ear', self, shortcut = QtGui.QKeySequence.Delete, triggered = self.clear_selected) self.edit.addAction(self.clear_action) self.transform = menu.addMenu('&Transform') self.shift_up_action = QtGui.QAction('Shift layers &upward', self, shortcut = QtGui.QKeySequence('Ctrl+Shift+Q'), triggered = self.shift_up) self.transform.addAction(self.shift_up_action) self.shift_down_action = QtGui.QAction('Shift layers &downward', self, shortcut = QtGui.QKeySequence('Ctrl+Shift+A'), triggered = self.shift_down) self.transform.addAction(self.shift_down_action) self.transform.addSeparator() self.mirror_horizontal_action = QtGui.QAction('Mirror &horizontal', self, triggered = self.mirror_horizontal) self.transform.addAction(self.mirror_horizontal_action) self.mirror_vertical_action = QtGui.QAction('Mirror &vertical', self, triggered = self.mirror_vertical) self.transform.addAction(self.mirror_vertical_action) self.mirror_both_action = QtGui.QAction('Mirror &both', self, triggered = self.mirror_both) self.transform.addAction(self.mirror_both_action) self.mirror_z_action = QtGui.QAction('Mirror &Z', self, triggered = self.mirror_z) self.transform.addAction(self.mirror_z_action) self.transform.addSeparator() self.rotate_90_CW_action = QtGui.QAction('Rotate &90\xb0 CW', self, triggered = self.rotate_90_CW) self.transform.addAction(self.rotate_90_CW_action) self.rotate_90_CCW_action = QtGui.QAction('Rotate 9&0\xb0 CCW', self, triggered = self.rotate_90_CCW) self.transform.addAction(self.rotate_90_CCW_action) self.rotate_180_action = QtGui.QAction('Rotate &180\xb0', self, triggered = self.rotate_180) self.transform.addAction(self.rotate_180_action) self.heightmap = menu.addMenu('&Heightmap') self.additive_heightmap_action = QtGui.QAction('&Additive...', self, triggered = self.generate_heightmap) self.heightmap.addAction(self.additive_heightmap_action) self.subtractive_heightmap_action = QtGui.QAction('&Subtractive...', self, triggered = self.subtractive_heightmap) self.heightmap.addAction(self.subtractive_heightmap_action) self.scroll_view = ScrollArea(self) self.edit_widget = EditWidget(self) self.scroll_view.setWidget(self.edit_widget) self.setCentralWidget(self.scroll_view) self.scroll_view.setAlignment(Qt.AlignCenter) self.apply_default() self.path = DEFAULT_PATH self.filename = None self.settings_dock = QtGui.QDockWidget(self) self.settings_dock.setWidget(Settings(self)) self.settings_dock.setWindowTitle('Settings') for item in (self.settings_dock,): self.addDockWidget(Qt.RightDockWidgetArea, item) def set_dirty(self, value = True): if self.dirty == value: return self.dirty = value self.save_action.setEnabled(value) def new_selected(self): msgBox = QMessageBox() msgBox.setText("Do you want to discard your changes?") msgBox.setStandardButtons(QMessageBox.Discard | QMessageBox.Cancel) msgBox.setDefaultButton(QMessageBox.Cancel) ret = msgBox.exec_() if ret == QMessageBox.Cancel: return self.filename = None self.apply_default() def open_selected(self): name = QtGui.QFileDialog.getOpenFileName(self, 'Open', self.path, filter = '*.vxl')[0] if not name: return self.filename = name self.map = VXLData(open(name, 'rb')) self.slice_map() self.edit_widget.map_updated(self.map) self.set_dirty(False) def apply_default(self): self.map = VXLData() self.slice_map(show_dialog = False) bottom_layer = self.layers[63] bottom_layer.fill(WATER_PEN.rgba()) bottom_layer.dirty = True self.edit_widget.map_updated(self.map) self.set_dirty(False) def slice_map(self, show_dialog = True): self.layers = [] if show_dialog: progress = progress_dialog(self.edit_widget, 0, 63, 'Slicing...', can_abort = False) for z in xrange(0, 64): if show_dialog: progress.setValue(z) self.layers.append(MapImage(self.map.get_overview(z), 512, 512, QImage.Format_ARGB32)) def save_selected(self): if self.filename is None: return self.save_as_selected() name = os.path.join(self.path, self.filename) self.save(name) return name def save_as_selected(self): path = os.path.join(self.path, self.filename or DEFAULT_FILENAME) name = QtGui.QFileDialog.getSaveFileName(self, 'Save As', path, filter = '*.vxl')[0] if not name: return self.path, self.filename = os.path.split(name) self.save(name) return name def save(self, filename): for z in xrange(0, 64): layer = self.layers[z] if layer.dirty: self.map.set_overview(layer.data, z) layer.dirty = False open(filename, 'wb').write(self.map.generate()) self.set_dirty(False) def open_voxed(self): name = self.save_selected() if not name: return if self.voxed_filename is None: default_path = 'C:\\Ace of Spades\\voxed.exe' if os.path.exists(default_path): self.voxed_filename = default_path else: exename = QtGui.QFileDialog.getOpenFileName(self, 'Select VOXED.EXE', filter = '*.exe')[0] if not exename: return self.voxed_filename = exename subprocess.call([self.voxed_filename, name]) def import_image_sequence(self): name = QtGui.QFileDialog.getOpenFileName(self, 'Import image sequence (select any file from the sequence)', filter = IMAGE_OPEN_FILTER)[0] if not name: return root, ext = os.path.splitext(name) head, tail = os.path.split(root) path = os.path.join(root, head, tail[:-2]) old_z = self.edit_widget.z progress = progress_dialog(self.edit_widget, 0, 63, 'Importing images...') for z in xrange(0, 64): if progress.wasCanceled(): break progress.setValue(z) image = QImage(path + format(z, '02d') + ext) if not image: continue interpret_colorkey(image) self.layers[63 - z].set_image(image) self.edit_widget.repaint() self.set_dirty() def export_color_map(self): name = QtGui.QFileDialog.getSaveFileName(self, 'Export Colormap', filter = IMAGE_SAVE_FILTER)[0] if not name: return color_image = QImage(512, 512, QImage.Format_ARGB32) color_lines = [] height_found = [] for y in xrange(0, 512): color_lines.append(color_image.scanLine(y)) height_found.append([]) for x in xrange(0, 512): height_found[y].append(False) progress = progress_dialog(self.edit_widget, 0, 63, 'Exporting Colormap...') for z in xrange(0, 64): if progress.wasCanceled(): break progress.setValue(z) image = self.layers[z] for y in xrange(0, 512): image_line = image.scanLine(y) color_line = color_lines[y] for x in xrange(0, 512): if height_found[y][x] is False: s = x * 4 image_pixel = image_line[s:s + 4] if image_pixel != TRANSPARENT_PACKED: height_found[y][x] = True color_line[s:s + 4] = image_pixel color_image.save(name) def export_height_map(self): name = QtGui.QFileDialog.getSaveFileName(self, 'Export Heightmap', filter = IMAGE_SAVE_FILTER)[0] if not name: return height_packed = [] for z in xrange(0, 64): height = (63 - z) * 4 height_packed.append(struct.pack('I', QtGui.qRgba(height, height, height, 255))) height_image = QImage(512, 512, QImage.Format_ARGB32) height_lines = [] height_found = [] for y in xrange(0, 512): height_lines.append(height_image.scanLine(y)) height_found.append([]) for x in xrange(0, 512): height_found[y].append(False) progress = progress_dialog(self.edit_widget, 0, 63, 'Exporting Heightmap...') for z in xrange(0, 64): if progress.wasCanceled(): break progress.setValue(z) packed_value = height_packed[z] image = self.layers[z] for y in xrange(0, 512): image_line = image.scanLine(y) height_line = height_lines[y] for x in xrange(0, 512): if height_found[y][x] is False: s = x * 4 if image_line[s:s + 4] != TRANSPARENT_PACKED: height_found[y][x] = True height_line[s:s + 4] = packed_value height_image.save(name) def export_image_sequence(self): name = QtGui.QFileDialog.getSaveFileName(self, 'Export image sequence (select base filename)', filter = IMAGE_SAVE_FILTER)[0] if not name: return root, ext = os.path.splitext(name) progress = progress_dialog(self.edit_widget, 0, 63, 'Exporting images...') for z in xrange(0, 64): if progress.wasCanceled(): break progress.setValue(z) image = self.layers[63 - z] new_image = make_colorkey(image) new_image.save(root + format(z, '02d') + ext) def copy_selected(self): self.clipboard.setImage(self.edit_widget.image) def paste_selected(self): image = self.clipboard.image() if not image: return self.edit_widget.set_image(image) self.set_dirty() def copy_external_selected(self): image = self.edit_widget.image new_image = make_colorkey(image) self.clipboard.setImage(new_image) def paste_external_selected(self): image = self.clipboard.image() if not image: return image = image.convertToFormat(QImage.Format_ARGB32) interpret_colorkey(image) self.edit_widget.set_image(image) self.set_dirty() def mirror(self, hor, ver): progress = progress_dialog(self.edit_widget, 0, 63, 'Mirroring...') for z in xrange(0, 64): if progress.wasCanceled(): break progress.setValue(z) layer = self.layers[z] layer.set_image(layer.mirrored(hor, ver)) self.edit_widget.repaint() self.set_dirty() def mirror_horizontal(self): self.mirror(True, False) def mirror_vertical(self): self.mirror(False, True) def mirror_both(self): self.mirror(True, True) def mirror_z(self): self.layers.reverse() for layer in self.layers: layer.dirty = True self.edit_widget.layers_updated() self.set_dirty() def rotate(self, angle): progress = progress_dialog(self.edit_widget, 0, 63, 'Rotating...') transform = QtGui.QTransform() transform.rotate(angle) for z in xrange(0, 64): if progress.wasCanceled(): break progress.setValue(z) layer = self.layers[z] layer.set_image(layer.transformed(transform)) self.edit_widget.repaint() self.set_dirty() def rotate_90_CW(self): self.rotate(90) def rotate_90_CCW(self): self.rotate(-90) def rotate_180(self): self.rotate(180) def cycle_layers(self, n): self.layers = self.layers[n:] + self.layers[:n] for layer in self.layers: layer.dirty = True self.edit_widget.layers_updated() self.set_dirty() def shift_up(self): self.cycle_layers(1) def shift_down(self): self.cycle_layers(-1) def clear_selected(self): self.edit_widget.clear() self.set_dirty() def get_height(self, color): return int(math.floor((float(QtGui.qRed(color)) + float(QtGui.qGreen(color)) + float(QtGui.qBlue(color)))/12.0)) def generate_heightmap(self, delete = False): h_name = QtGui.QFileDialog.getOpenFileName(self, 'Select heightmap file', filter = IMAGE_OPEN_FILTER)[0] if not h_name: return h_image = QImage(h_name) if not delete: c_name = QtGui.QFileDialog.getOpenFileName(self, 'Select colormap file', filter = IMAGE_OPEN_FILTER)[0] if not c_name: return c_image = QImage(c_name) height_values = [] color_lines = [] for y in xrange(0, 512): height_values.append([]) height_line = h_image.scanLine(y) if not delete: color_lines.append(c_image.scanLine(y)) for x in xrange(0,512): height_values[y].append(self.get_height(struct.unpack('I', height_line[x * 4:x * 4 + 4])[0])) progress = progress_dialog(self.edit_widget, 0, 63, 'Generating from heightmap...') for z in xrange(0, 64): if progress.wasCanceled(): break progress.setValue(z) if z == 0 and delete: continue image = self.layers[63 - z] for y in xrange(0, 512): image_line = image.scanLine(y) if not delete: color_line = color_lines[y] for x in xrange(0, 512): if z <= height_values[y][x]: s = x * 4 if not delete: image_line[s:s + 4] = color_line[s:s + 4] else: image_line[s:s + 4] = TRANSPARENT_PACKED image.dirty = True self.set_dirty() def subtractive_heightmap(self): return self.generate_heightmap(True) def quit(self): if self.dirty: text = ('Save changes to ' + (self.filename or DEFAULT_FILENAME) + ' before closing?') reply = QMessageBox.warning(self, self.app.applicationName(), text, QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel, QMessageBox.Yes) if reply == QMessageBox.Yes: if not self.save_selected(): return elif reply == QMessageBox.Cancel: return self.app.exit()
class MapEditor(QtGui.QMainWindow): path = None filename = None voxed_filename = None layers = None dirty = False def __init__(self, app, *arg, **kw): super(MapEditor, self).__init__(*arg, **kw) self.app = app self.app.setApplicationName('pyspades map editor') self.setWindowTitle('pyspades map editor') self.clipboard = app.clipboard() menu = self.menuBar() self.file = menu.addMenu('&File') self.new_action = QtGui.QAction('&New', self, shortcut = QtGui.QKeySequence('Ctrl+N'), triggered = self.new_selected) self.file.addAction(self.new_action) self.open_action = QtGui.QAction('&Open...', self, shortcut = QtGui.QKeySequence('Ctrl+O'), triggered = self.open_selected) self.file.addAction(self.open_action) self.file.addSeparator() self.save_action = QtGui.QAction('&Save', self, shortcut = QtGui.QKeySequence.Save, triggered = self.save_selected) self.save_action.setEnabled(False) self.file.addAction(self.save_action) self.save_as_action = QtGui.QAction('Save &As...', self, shortcut = QtGui.QKeySequence('Ctrl+Shift+S'), triggered = self.save_as_selected) self.file.addAction(self.save_as_action) self.file.addSeparator() self.import_menu = self.file.addMenu('&Import') self.import_image_sequence_menu = QtGui.QAction('Image &sequence...', self, triggered = self.import_image_sequence) self.import_menu.addAction(self.import_image_sequence_menu) self.export = self.file.addMenu('&Export') self.color_map = QtGui.QAction('&Colormap...', self, triggered = self.export_color_map) self.export.addAction(self.color_map) self.height_map = QtGui.QAction('&Heightmap...', self, triggered = self.export_height_map) self.export.addAction(self.height_map) self.export_image_sequence_menu = QtGui.QAction('Image &sequence...', self, triggered = self.export_image_sequence) self.export.addAction(self.export_image_sequence_menu) self.file.addSeparator() self.bake_voxed_action = QtGui.QAction( '&Calculate shadows and open in VOXED', self, shortcut = QtGui.QKeySequence('F5'), triggered = self.bake_shadows_open_voxed) self.file.addAction(self.bake_voxed_action) self.voxed_action = QtGui.QAction('Open in &VOXED', self, shortcut = QtGui.QKeySequence('Shift+F5'), triggered = self.open_voxed) self.file.addAction(self.voxed_action) self.file.addSeparator() self.quit_action = QtGui.QAction('E&xit', self, shortcut = QtGui.QKeySequence('Ctrl+Q'), triggered = self.quit) self.file.addAction(self.quit_action) self.edit = menu.addMenu('&Edit') self.copy_action = QtGui.QAction('C&opy', self, shortcut = QtGui.QKeySequence.Copy, triggered = self.copy_selected) self.edit.addAction(self.copy_action) self.paste_action = QtGui.QAction('P&aste', self, shortcut = QtGui.QKeySequence.Paste, triggered = self.paste_selected) self.edit.addAction(self.paste_action) self.copy_action_external = QtGui.QAction('&Copy External', self, shortcut = QtGui.QKeySequence('Ctrl+Shift+C'), triggered = self.copy_external_selected) self.edit.addAction(self.copy_action_external) self.paste_action_external = QtGui.QAction('&Paste External', self, shortcut = QtGui.QKeySequence('Ctrl+Shift+V'), triggered = self.paste_external_selected) self.edit.addAction(self.paste_action_external) self.clear_action = QtGui.QAction('Cl&ear', self, shortcut = QtGui.QKeySequence.Delete, triggered = self.clear_selected) self.edit.addAction(self.clear_action) self.transform = menu.addMenu('&Transform') self.shift_up_action = QtGui.QAction('Shift layers &upward', self, shortcut = QtGui.QKeySequence('Ctrl+Shift+Q'), triggered = self.shift_up) self.transform.addAction(self.shift_up_action) self.shift_down_action = QtGui.QAction('Shift layers &downward', self, shortcut = QtGui.QKeySequence('Ctrl+Shift+A'), triggered = self.shift_down) self.transform.addAction(self.shift_down_action) self.transform.addSeparator() self.mirror_horizontal_action = QtGui.QAction('Mirror &horizontal', self, triggered = self.mirror_horizontal) self.transform.addAction(self.mirror_horizontal_action) self.mirror_vertical_action = QtGui.QAction('Mirror &vertical', self, triggered = self.mirror_vertical) self.transform.addAction(self.mirror_vertical_action) self.mirror_both_action = QtGui.QAction('Mirror &both', self, triggered = self.mirror_both) self.transform.addAction(self.mirror_both_action) self.mirror_z_action = QtGui.QAction('Mirror &Z', self, triggered = self.mirror_z) self.transform.addAction(self.mirror_z_action) self.transform.addSeparator() self.rotate_90_CW_action = QtGui.QAction('Rotate &90\xb0 CW', self, triggered = self.rotate_90_CW) self.transform.addAction(self.rotate_90_CW_action) self.rotate_90_CCW_action = QtGui.QAction('Rotate 9&0\xb0 CCW', self, triggered = self.rotate_90_CCW) self.transform.addAction(self.rotate_90_CCW_action) self.rotate_180_action = QtGui.QAction('Rotate &180\xb0', self, triggered = self.rotate_180) self.transform.addAction(self.rotate_180_action) self.heightmap = menu.addMenu('&Heightmap') self.additive_heightmap_action = QtGui.QAction('&Additive...', self, triggered = self.generate_heightmap) self.heightmap.addAction(self.additive_heightmap_action) self.subtractive_heightmap_action = QtGui.QAction('&Subtractive...', self, triggered = self.subtractive_heightmap) self.heightmap.addAction(self.subtractive_heightmap_action) self.scroll_view = ScrollArea(self) self.edit_widget = EditWidget(self) self.scroll_view.setWidget(self.edit_widget) self.setCentralWidget(self.scroll_view) self.scroll_view.setAlignment(Qt.AlignCenter) self.apply_default() self.path = DEFAULT_PATH self.filename = None self.settings_dock = QtGui.QDockWidget(self) self.settings_dock.setWidget(Settings(self)) self.settings_dock.setWindowTitle('Settings') for item in (self.settings_dock,): self.addDockWidget(Qt.RightDockWidgetArea, item) def set_dirty(self, value = True): if self.dirty == value: return self.dirty = value self.save_action.setEnabled(value) def new_selected(self): msgBox = QMessageBox() msgBox.setText("Do you want to discard your changes?") msgBox.setStandardButtons(QMessageBox.Discard | QMessageBox.Cancel) msgBox.setDefaultButton(QMessageBox.Cancel) ret = msgBox.exec_() if ret == QMessageBox.Cancel: return self.filename = None self.apply_default() def open_selected(self): name = QtGui.QFileDialog.getOpenFileName(self, 'Open', self.path, filter = '*.vxl')[0] if not name: return self.filename = name self.map = VXLData(open(name, 'rb')) self.slice_map() self.edit_widget.map_updated(self.map) self.set_dirty(False) def apply_default(self): self.map = VXLData() self.slice_map(show_dialog = False) bottom_layer = self.layers[63] bottom_layer.fill(WATER_PEN.rgba()) bottom_layer.dirty = True self.edit_widget.map_updated(self.map) self.set_dirty(False) def slice_map(self, show_dialog = True): self.layers = [] if show_dialog: progress = progress_dialog(self.edit_widget, 0, 63, 'Slicing...', can_abort = False) for z in xrange(0, 64): if show_dialog: progress.setValue(z) self.layers.append(MapImage(self.map.get_overview(z), 512, 512, QImage.Format_ARGB32)) def save_selected(self): if self.filename is None: return self.save_as_selected() name = os.path.join(self.path, self.filename) self.save(name) return name def save_as_selected(self): path = os.path.join(self.path, self.filename or DEFAULT_FILENAME) name = QtGui.QFileDialog.getSaveFileName(self, 'Save As', path, filter = '*.vxl')[0] if not name: return self.path, self.filename = os.path.split(name) self.save(name) return name def update_map(self): for z, layer in enumerate(self.layers): if layer.dirty: self.map.set_overview(layer.data, z) layer.dirty = False def save(self, filename): self.update_map() open(filename, 'wb').write(self.map.generate()) self.set_dirty(False) def open_voxed(self): name = self.save_selected() if not name: return if self.voxed_filename is None: default_path = 'C:\\Ace of Spades\\voxed.exe' if os.path.exists(default_path): self.voxed_filename = default_path else: exename = QtGui.QFileDialog.getOpenFileName(self, 'Select VOXED.EXE', filter = '*.exe')[0] if not exename: return self.voxed_filename = exename subprocess.call([self.voxed_filename, name]) def bake_shadows_open_voxed(self): self.update_map() self.map.update_shadows() self.open_voxed() def import_image_sequence(self): name = QtGui.QFileDialog.getOpenFileName(self, 'Import image sequence (select any file from the sequence)', filter = IMAGE_OPEN_FILTER)[0] if not name: return root, ext = os.path.splitext(name) head, tail = os.path.split(root) path = os.path.join(root, head, tail[:-2]) old_z = self.edit_widget.z progress = progress_dialog(self.edit_widget, 0, 63, 'Importing images...') for z, layer in enumerate(reversed(self.layers)): if progress.wasCanceled(): break progress.setValue(z) image = QImage(path + format(z, '02d') + ext) if not image: continue interpret_colorkey(image) layer.set_image(image) self.edit_widget.repaint() self.set_dirty() def export_color_map(self): name = QtGui.QFileDialog.getSaveFileName(self, 'Export Colormap', filter = IMAGE_SAVE_FILTER)[0] if not name: return color_image = QImage(512, 512, QImage.Format_ARGB32) color_lines = [] height_found = [] for y in xrange(0, 512): color_lines.append(color_image.scanLine(y)) height_found.append([]) for x in xrange(0, 512): height_found[y].append(False) progress = progress_dialog(self.edit_widget, 0, 63, 'Exporting Colormap...') for z, image in enumerate(self.layers): if progress.wasCanceled(): break progress.setValue(z) for y in xrange(0, 512): image_line = image.scanLine(y) color_line = color_lines[y] for x in xrange(0, 512): if height_found[y][x] is False: s = x * 4 image_pixel = image_line[s:s + 4] if image_pixel != TRANSPARENT_PACKED: height_found[y][x] = True color_line[s:s + 4] = image_pixel color_image.save(name) def export_height_map(self): name = QtGui.QFileDialog.getSaveFileName(self, 'Export Heightmap', filter = IMAGE_SAVE_FILTER)[0] if not name: return height_packed = [] for z in xrange(0, 64): height = (63 - z) * 4 height_packed.append(struct.pack('I', QtGui.qRgba(height, height, height, 255))) height_image = QImage(512, 512, QImage.Format_ARGB32) height_lines = [] height_found = [] for y in xrange(0, 512): height_lines.append(height_image.scanLine(y)) height_found.append([]) for x in xrange(0, 512): height_found[y].append(False) progress = progress_dialog(self.edit_widget, 0, 63, 'Exporting Heightmap...') for z, image in enumerate(self.layers): if progress.wasCanceled(): break progress.setValue(z) packed_value = height_packed[z] for y in xrange(0, 512): image_line = image.scanLine(y) height_line = height_lines[y] for x in xrange(0, 512): if height_found[y][x] is False: s = x * 4 if image_line[s:s + 4] != TRANSPARENT_PACKED: height_found[y][x] = True height_line[s:s + 4] = packed_value height_image.save(name) def export_image_sequence(self): name = QtGui.QFileDialog.getSaveFileName(self, 'Export image sequence (select base filename)', filter = IMAGE_SAVE_FILTER)[0] if not name: return root, ext = os.path.splitext(name) head, tail = os.path.split(root) path = os.path.join(root, head, re.sub('\d{2}$', '', tail)) progress = progress_dialog(self.edit_widget, 0, 63, 'Exporting images...') for z, image in enumerate(reversed(self.layers)): if progress.wasCanceled(): break progress.setValue(z) new_image = make_colorkey(image) new_image.save(path + format(z, '02d') + ext) def copy_selected(self): self.clipboard.setImage(self.edit_widget.image) def paste_selected(self): image = self.clipboard.image() if not image: return self.edit_widget.set_image(image) self.set_dirty() def copy_external_selected(self): image = self.edit_widget.image new_image = make_colorkey(image) self.clipboard.setImage(new_image) def paste_external_selected(self): image = self.clipboard.image() if not image: return image = image.convertToFormat(QImage.Format_ARGB32) interpret_colorkey(image) self.edit_widget.set_image(image) self.set_dirty() def mirror(self, hor, ver): progress = progress_dialog(self.edit_widget, 0, 63, 'Mirroring...') for z, layer in enumerate(self.layers): if progress.wasCanceled(): break progress.setValue(z) layer.set_image(layer.mirrored(hor, ver)) self.edit_widget.repaint() self.set_dirty() def mirror_horizontal(self): self.mirror(True, False) def mirror_vertical(self): self.mirror(False, True) def mirror_both(self): self.mirror(True, True) def mirror_z(self): self.layers.reverse() for layer in self.layers: layer.dirty = True self.edit_widget.layers_updated() self.set_dirty() def rotate(self, angle): progress = progress_dialog(self.edit_widget, 0, 63, 'Rotating...') transform = QtGui.QTransform() transform.rotate(angle) for z, layer in enumerate(self.layers): if progress.wasCanceled(): break progress.setValue(z) layer.set_image(layer.transformed(transform)) self.edit_widget.repaint() self.set_dirty() def rotate_90_CW(self): self.rotate(90) def rotate_90_CCW(self): self.rotate(-90) def rotate_180(self): self.rotate(180) def cycle_layers(self, n): self.layers = self.layers[n:] + self.layers[:n] for layer in self.layers: layer.dirty = True self.edit_widget.layers_updated() self.set_dirty() def shift_up(self): self.cycle_layers(1) def shift_down(self): self.cycle_layers(-1) def clear_selected(self): self.edit_widget.clear() self.set_dirty() def get_height(self, color): return int(math.floor((float(QtGui.qRed(color)) + float(QtGui.qGreen(color)) + float(QtGui.qBlue(color))) / 12.0)) def generate_heightmap(self, delete = False): h_name = QtGui.QFileDialog.getOpenFileName(self, 'Select heightmap file', filter = IMAGE_OPEN_FILTER)[0] if not h_name: return h_image = QImage(h_name) if not delete: c_name = QtGui.QFileDialog.getOpenFileName(self, 'Select colormap file', filter = IMAGE_OPEN_FILTER)[0] if not c_name: return c_image = QImage(c_name) height_values = [] color_lines = [] for y in xrange(0, 512): height_values.append([]) height_line = h_image.scanLine(y) if not delete: color_lines.append(c_image.scanLine(y)) for x in xrange(0,512): height_values[y].append(self.get_height( struct.unpack('I', height_line[x * 4:x * 4 + 4])[0])) progress = progress_dialog(self.edit_widget, 0, 63, 'Generating from heightmap...') for z, image in enumerate(reversed(self.layers)): if progress.wasCanceled(): break progress.setValue(z) if z == 0 and delete: continue for y in xrange(0, 512): image_line = image.scanLine(y) if not delete: color_line = color_lines[y] for x in xrange(0, 512): if z <= height_values[y][x]: s = x * 4 if not delete: image_line[s:s + 4] = color_line[s:s + 4] else: image_line[s:s + 4] = TRANSPARENT_PACKED image.dirty = True self.set_dirty() def subtractive_heightmap(self): return self.generate_heightmap(True) def quit(self): if self.dirty: text = ('Save changes to ' + (self.filename or DEFAULT_FILENAME) + ' before closing?') reply = QMessageBox.warning(self, self.app.applicationName(), text, QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel, QMessageBox.Yes) if reply == QMessageBox.Cancel: return elif reply == QMessageBox.Yes and not self.save_selected(): return self.app.exit()