chiark / gitweb /
Merge tag '15.01-RC1' into upstream
[cura.git] / Cura / gui / tools / minecraftImport.py
1 """
2 Tool to import sections of minecraft levels into Cura.
3 This makes use of the pymclevel module from David Rio Vierra
4 """
5 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
6
7 import wx
8 import glob
9 import os
10 import numpy
11
12 from Cura.util import printableObject
13 from Cura.util.meshLoaders import stl
14 from Cura.util.pymclevel import mclevel
15
16 def hasMinecraft():
17         return os.path.isdir(mclevel.saveFileDir)
18
19 class minecraftImportWindow(wx.Frame):
20         def __init__(self, parent):
21                 super(minecraftImportWindow, self).__init__(parent, title=_('Cura - Minecraft import'))
22
23                 saveFileList = map(os.path.basename, glob.glob(mclevel.saveFileDir + "/*"))
24
25                 self.panel = wx.Panel(self, -1)
26                 self.SetSizer(wx.BoxSizer())
27                 self.GetSizer().Add(self.panel, 1, wx.EXPAND)
28
29                 sizer = wx.GridBagSizer(2, 2)
30
31                 self.saveListBox = wx.ListBox(self.panel, -1, choices=saveFileList)
32                 sizer.Add(self.saveListBox, (0,0), span=(2,1), flag=wx.EXPAND)
33                 self.playerListBox = wx.ListBox(self.panel, -1, choices=[])
34                 sizer.Add(self.playerListBox, (0,1), span=(2,1), flag=wx.EXPAND)
35
36                 self.previewPanel = wx.Panel(self.panel, -1)
37                 self.previewPanel.SetMinSize((512, 512))
38                 sizer.Add(self.previewPanel, (0,2), flag=wx.EXPAND)
39
40                 self.importButton = wx.Button(self.panel, -1, _('Import'))
41                 sizer.Add(self.importButton, (1,2))
42
43                 sizer.AddGrowableRow(1)
44
45                 self.panel.SetSizer(sizer)
46
47                 self.saveListBox.Bind(wx.EVT_LISTBOX, self.OnSaveSelect)
48                 self.playerListBox.Bind(wx.EVT_LISTBOX, self.OnPlayerSelect)
49                 self.importButton.Bind(wx.EVT_BUTTON, self.OnImport)
50
51                 self.previewPanel.Bind(wx.EVT_PAINT, self.OnPaintPreview)
52                 self.previewPanel.Bind(wx.EVT_SIZE, self.OnSizePreview)
53                 self.previewPanel.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackgroundPreview)
54                 self.previewPanel.Bind(wx.EVT_MOTION, self.OnMotion)
55
56                 self.level = None
57                 self.previewImage = None
58                 self.renderList = []
59                 self.selectArea = None
60                 self.draggingArea = False
61
62                 self.Layout()
63                 self.Fit()
64
65                 self.gravelPen = wx.Pen(wx.Colour(128, 128, 128))
66                 self.sandPen = wx.Pen(wx.Colour(192, 192, 0))
67                 self.grassPen = []
68                 self.waterPen = []
69                 for z in xrange(0, 256):
70                         self.waterPen.append(wx.Pen(wx.Colour(0,0,min(z+64, 255))))
71                         self.grassPen.append(wx.Pen(wx.Colour(0,min(64+z,255),0)))
72
73                 self.isSolid = [True] * 256
74                 self.isSolid[0] = False #Air
75                 self.isSolid[8] = False #Water
76                 self.isSolid[9] = False #Water
77                 self.isSolid[10] = False #Lava
78                 self.isSolid[11] = False #Lava
79                 self.isSolid[50] = False #Torch
80                 self.isSolid[51] = False #Fire
81
82         def OnSaveSelect(self, e):
83                 if self.saveListBox.Selection < 0:
84                         return
85                 self.level = mclevel.loadWorld(self.saveListBox.GetItems()[self.saveListBox.Selection])
86                 self.playerListBox.Clear()
87                 for player in self.level.players:
88                         self.playerListBox.Append(player)
89
90         def OnPlayerSelect(self, e):
91                 playerName = self.playerListBox.GetItems()[self.playerListBox.Selection]
92                 self.playerPos = map(lambda n: int(n / 16), self.level.getPlayerPosition(playerName))[0::2]
93
94                 self.previewImage = wx.EmptyBitmap(512, 512)
95                 for i in xrange(0, 16):
96                         for j in xrange(1, i * 2 + 1):
97                                 self.renderList.insert(0, (15 - i, 16 + i - j))
98                         for j in xrange(0, i * 2 + 1):
99                                 self.renderList.insert(0, (15 + j - i, 15 - i))
100                         for j in xrange(0, i * 2 + 1):
101                                 self.renderList.insert(0, (16 + i, 15 + j - i))
102                         for j in xrange(0, i * 2 + 2):
103                                 self.renderList.insert(0, (16 + i - j, 16 + i))
104                 self.previewPanel.Refresh()
105
106         def OnPaintPreview(self, e):
107                 if len(self.renderList) > 0:
108                         cx, cy = self.renderList.pop()
109                         dc = wx.MemoryDC()
110                         dc.SelectObject(self.previewImage)
111                         chunk = self.level.getChunk(cx + self.playerPos[0] - 16, cy + self.playerPos[1] - 16)
112                         dc.SetPen(wx.Pen(wx.Colour(255,0,0)))
113                         for x in xrange(0, 16):
114                                 for y in xrange(0, 16):
115                                         z = numpy.max(numpy.where(chunk.Blocks[x, y] != 0))
116                                         type = chunk.Blocks[x, y, z]
117                                         if type == 1:    #Stone
118                                                 dc.SetPen(wx.Pen(wx.Colour(z,z,z)))
119                                         elif type == 2:    #Grass
120                                                 dc.SetPen(self.grassPen[z])
121                                         elif type == 8 or type == 9: #Water
122                                                 dc.SetPen(self.waterPen[z])
123                                         elif type == 10 or type == 11: #Lava
124                                                 dc.SetPen(wx.Pen(wx.Colour(min(z+64, 255),0,0)))
125                                         elif type == 12 or type == 24: #Sand/Standstone
126                                                 dc.SetPen(self.sandPen)
127                                         elif type == 13: #Gravel
128                                                 dc.SetPen(self.gravelPen)
129                                         elif type == 18: #Leaves
130                                                 dc.SetPen(wx.Pen(wx.Colour(0,max(z-32, 0),0)))
131                                         else:
132                                                 dc.SetPen(wx.Pen(wx.Colour(z,z,z)))
133                                         dc.DrawPoint(cx * 16 + x, cy * 16 + y)
134                         dc.SelectObject(wx.NullBitmap)
135                         wx.CallAfter(self.previewPanel.Refresh)
136
137                 dc = wx.BufferedPaintDC(self.previewPanel)
138                 dc.SetBackground(wx.Brush(wx.BLACK))
139                 dc.Clear()
140                 if self.previewImage is not None:
141                         dc.DrawBitmap(self.previewImage, 0, 0)
142                 if self.selectArea is not None:
143                         dc.SetPen(wx.Pen(wx.Colour(255,0,0)))
144                         dc.SetBrush(wx.Brush(None, style=wx.TRANSPARENT))
145                         dc.DrawRectangle(self.selectArea[0], self.selectArea[1], self.selectArea[2] - self.selectArea[0] + 1, self.selectArea[3] - self.selectArea[1] + 1)
146
147         def OnSizePreview(self, e):
148                 self.previewPanel.Refresh()
149                 self.previewPanel.Update()
150
151         def OnEraseBackgroundPreview(self, e):
152                 pass
153
154         def OnMotion(self, e):
155                 if e.Dragging():
156                         if not self.draggingArea:
157                                 self.draggingArea = True
158                                 self.selectArea = [e.GetX(), e.GetY(), e.GetX(), e.GetY()]
159                         self.selectArea[2] = e.GetX()
160                         self.selectArea[3] = e.GetY()
161                         self.previewPanel.Refresh()
162                 else:
163                         self.draggingArea = False
164
165         def OnImport(self, e):
166                 if self.level is None or self.selectArea is None:
167                         return
168
169                 xMin = min(self.selectArea[0], self.selectArea[2]) + (self.playerPos[0] - 16) * 16
170                 xMax = max(self.selectArea[0], self.selectArea[2]) + (self.playerPos[0] - 16) * 16
171                 yMin = min(self.selectArea[1], self.selectArea[3]) + (self.playerPos[1] - 16) * 16
172                 yMax = max(self.selectArea[1], self.selectArea[3]) + (self.playerPos[1] - 16) * 16
173
174                 sx = (xMax - xMin + 1)
175                 sy = (yMax - yMin + 1)
176                 blocks = numpy.zeros((sx, sy, 256), numpy.int32)
177
178                 cxMin = int(xMin / 16)
179                 cxMax = int((xMax + 15) / 16)
180                 cyMin = int(yMin / 16)
181                 cyMax = int((yMax + 15) / 16)
182
183                 for cx in xrange(cxMin, cxMax + 1):
184                         for cy in xrange(cyMin, cyMax + 1):
185                                 chunk = self.level.getChunk(cx, cy)
186                                 for x in xrange(0, 16):
187                                         bx = x + cx * 16
188                                         if xMin <= bx <= xMax:
189                                                 for y in xrange(0, 16):
190                                                         by = y + cy * 16
191                                                         if yMin <= by <= yMax:
192                                                                 blocks[bx - xMin, by - yMin] = chunk.Blocks[x, y]
193                 minZ = 256
194                 maxZ = 0
195                 for x in xrange(0, sx):
196                         for y in xrange(0, sy):
197                                 minZ = min(minZ, numpy.max(numpy.where(blocks[x, y] != 0)))
198                                 maxZ = max(maxZ, numpy.max(numpy.where(blocks[x, y] != 0)))
199                 minZ += 1
200
201                 faceCount = 0
202                 for x in xrange(0, sx):
203                         for y in xrange(0, sy):
204                                 for z in xrange(minZ, maxZ + 1):
205                                         if self.isSolid[blocks[x, y, z]]:
206                                                 if z == maxZ or not self.isSolid[blocks[x, y, z + 1]]:
207                                                         faceCount += 1
208                                                 if z == minZ or not self.isSolid[blocks[x, y, z - 1]]:
209                                                         faceCount += 1
210                                                 if x == 0 or not self.isSolid[blocks[x - 1, y, z]]:
211                                                         faceCount += 1
212                                                 if x == sx - 1 or not self.isSolid[blocks[x + 1, y, z]]:
213                                                         faceCount += 1
214                                                 if y == 0 or not self.isSolid[blocks[x, y - 1, z]]:
215                                                         faceCount += 1
216                                                 if y == sy - 1 or not self.isSolid[blocks[x, y + 1, z]]:
217                                                         faceCount += 1
218
219                 obj = printableObject.printableObject(None)
220                 m = obj._addMesh()
221                 m._prepareFaceCount(faceCount * 2)
222                 for x in xrange(0, sx):
223                         for y in xrange(0, sy):
224                                 for z in xrange(minZ, maxZ + 1):
225                                         if self.isSolid[blocks[x, y, z]]:
226                                                 if z == maxZ or not self.isSolid[blocks[x, y, z + 1]]:
227                                                         m._addFace(x, y, z+1, x+1, y, z+1, x, y+1, z+1)
228
229                                                         m._addFace(x+1, y+1, z+1, x, y+1, z+1, x+1, y, z+1)
230
231                                                 if z == minZ or not self.isSolid[blocks[x, y, z - 1]]:
232                                                         m._addFace(x, y, z, x, y+1, z, x+1, y, z)
233
234                                                         m._addFace(x+1, y+1, z, x+1, y, z, x, y+1, z)
235
236                                                 if x == 0 or not self.isSolid[blocks[x - 1, y, z]]:
237                                                         m._addFace(x, y, z, x, y, z+1, x, y+1, z)
238
239                                                         m._addFace(x, y+1, z+1, x, y+1, z, x, y, z+1)
240
241                                                 if x == sx - 1 or not self.isSolid[blocks[x + 1, y, z]]:
242                                                         m._addFace(x+1, y, z, x+1, y+1, z, x+1, y, z+1)
243
244                                                         m._addFace(x+1, y+1, z+1, x+1, y, z+1, x+1, y+1, z)
245
246                                                 if y == 0 or not self.isSolid[blocks[x, y - 1, z]]:
247                                                         m._addFace(x, y, z, x+1, y, z, x, y, z+1)
248
249                                                         m._addFace(x+1, y, z+1, x, y, z+1, x+1, y, z)
250
251                                                 if y == sy - 1 or not self.isSolid[blocks[x, y + 1, z]]:
252                                                         m._addFace(x, y+1, z, x, y+1, z+1, x+1, y+1, z)
253
254                                                         m._addFace(x+1, y+1, z+1, x+1, y+1, z, x, y+1, z+1)
255
256                 obj._postProcessAfterLoad()
257                 self.GetParent().scene._scene.add(obj)