invert scale
[blender-addons.git] / io_import_gimp_image_to_scene.py
blob71e8d3ed4640a641349cbb9bf9bd785c4f8fc8de
1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
19 bl_info = {
20 "name": "Import GIMP Image to Scene (.xcf/.xjt)",
21 "author": "Daniel Salazar (ZanQdo)",
22 "version": (2, 0, 0),
23 "blender": (2, 57, 0),
24 "location": "File > Import > GIMP Image to Scene(.xcf/.xjt)",
25 "description": "Imports GIMP multilayer image files as a series of multiple planes",
26 "warning": "XCF import requires xcftools installed",
27 "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
28 "Scripts/Import-Export/GIMPImageToScene",
29 "tracker_url": "http://projects.blender.org/tracker/index.php?"
30 "func=detail&aid=25136",
31 "category": "Import-Export"}
33 """
34 This script imports GIMP layered image files into 3D Scenes (.xcf, .xjt)
35 """
37 def main(File, Path, LayerViewers, MixerViewers, LayerOffset,
38 LayerScale, OpacityMode, AlphaMode, ShadelessMats,
39 SetCamera, SetupCompo, GroupUntagged, Ext):
41 #-------------------------------------------------
43 #Folder = '['+File.rstrip(Ext)+']'+'_images/'
44 Folder = 'images_'+'['+File.rstrip(Ext)+']/'
46 if not bpy.data.is_saved:
47 PathSaveRaw = Path+Folder
48 PathSave = PathSaveRaw.replace(' ', '\ ')
49 try: os.mkdir(PathSaveRaw)
50 except: pass
51 else:
52 PathSave = bpy.data.filepath
53 RSlash = PathSave.rfind('/')
54 PathSaveRaw = PathSave[:RSlash+1]+Folder
55 PathSave = PathSaveRaw.replace(' ', '\ ')
56 try: os.mkdir(PathSaveRaw)
57 except: pass
58 PathSaveRaw = bpy.path.relpath(PathSaveRaw)+'/'
60 PathRaw = Path
61 Path = Path.replace(' ', '\ ')
62 if Ext == '.xjt':
63 ExtSave = '.jpg'
64 #-------------------------------------------------
65 # EXTRACT XJT
66 import tarfile
68 IMG = tarfile.open ('%s%s' % (PathRaw, File))
69 PRP = IMG.extractfile('PRP')
71 Members = IMG.getmembers()
73 for Member in Members:
74 Name = Member.name
75 if Name.startswith('l') and Name.endswith('.jpg'):
76 IMG.extract(Name, path=PathSaveRaw)
78 #-------------------------------------------------
79 # INFO XJT
80 IMGs = []
81 for Line in PRP.readlines():
82 Line = str(Line)
84 if Line.startswith("b'GIMP_XJ_IMAGE"):
85 for Segment in Line.split():
86 if Segment.startswith('w/h:'):
87 ResX, ResY = map (int, Segment[4:].split(','))
88 if Line.startswith(("b'L", "b'l")):
90 """The "nice" method to check if layer has alpha channel
91 sadly GIMP sometimes decides not to export an alpha channel
92 if it's pure white so we are not completly sure here yet"""
93 if Line.startswith("b'L"): HasAlpha = True
94 else: HasAlpha = False
96 md = None
97 op = 1
98 ox, oy = 0,0
100 for Segment in Line.split():
102 if Segment.startswith("b'"):
103 imageFile = 'l' + Segment[3:] + '.jpg'
104 imageFileAlpha ='la'+Segment[3:]+'.jpg'
106 """Phisically double checking if alpha image exists
107 now we can be sure! (damn GIMP)"""
108 if HasAlpha:
109 if not os.path.isfile(PathSaveRaw+imageFileAlpha): HasAlpha = False
111 # Get Widht and Height from images
112 data = open(PathSaveRaw+imageFile, "rb").read()
114 hexList = []
115 for ch in data:
116 byt = "%02X" % ch
117 hexList.append(byt)
119 for k in range(len(hexList)-1):
120 if hexList[k] == 'FF' and (hexList[k+1] == 'C0' or hexList[k+1] == 'C2'):
121 ow = int(hexList[k+7],16)*256 + int(hexList[k+8],16)
122 oh = int(hexList[k+5],16)*256 + int(hexList[k+6],16)
124 elif Segment.startswith('md:'): # mode
125 md = Segment[3:]
127 elif Segment.startswith('op:'): # opacity
128 op = float(Segment[3:])*.01
130 elif Segment.startswith('o:'): # origin
131 ox, oy = map(int, Segment[2:].split(','))
133 elif Segment.startswith('n:'): # name
134 n = Segment[3:-4]
135 OpenBracket = n.find ('[')
136 CloseBracket = n.find (']')
138 if OpenBracket != -1 and CloseBracket != -1:
139 RenderLayer = n[OpenBracket+1:CloseBracket]
140 NameShort = n[:OpenBracket]
142 else:
143 RenderLayer = n
144 NameShort = n
146 os.rename(PathSaveRaw+imageFile, PathSaveRaw+NameShort+'.jpg')
147 if HasAlpha: os.rename(PathSaveRaw+imageFileAlpha, PathSaveRaw+NameShort+'_A'+'.jpg')
149 IMGs.append({'LayerMode':md, 'LayerOpacity':op,
150 'LayerName':n, 'LayerNameShort':NameShort,
151 'RenderLayer':RenderLayer, 'LayerCoords':[ow, oh, ox, oy], 'HasAlpha':HasAlpha})
153 else: # Ext == '.xcf':
154 ExtSave = '.png'
155 #-------------------------------------------------
156 # CONFIG
157 XCFInfo = 'xcfinfo'
158 XCF2PNG = 'xcf2png'
159 #-------------------------------------------------
160 # INFO XCF
162 CMD = '%s %s%s' % (XCFInfo, Path, File)
164 Info = os.popen(CMD)
166 IMGs = []
167 for Line in Info.readlines():
168 if Line.startswith ('+'):
170 Line = Line.split(' ', 4)
172 RenderLayer = Line[4]
174 OpenBracket = RenderLayer.find ('[')
175 CloseBracket = RenderLayer.find (']')
177 if OpenBracket != -1 and CloseBracket != -1:
178 RenderLayer = RenderLayer[OpenBracket+1:CloseBracket]
179 NameShort = Line[4][:OpenBracket]
180 else:
181 NameShort = Line[4].rstrip()
182 if GroupUntagged:
183 RenderLayer = '__Undefined__'
184 else:
185 RenderLayer = NameShort
187 LineThree = Line[3]
188 Slash = LineThree.find('/')
189 if Slash == -1:
190 Mode = LineThree
191 Opacity = 1
192 else:
193 Mode = LineThree[:Slash]
194 Opacity = float(LineThree[Slash+1:LineThree.find('%')])*.01
196 IMGs.append ({
197 'LayerMode': Mode,
198 'LayerOpacity': Opacity,
199 'LayerName': Line[4].rstrip(),
200 'LayerNameShort': NameShort,
201 'LayerCoords': list(map(int, Line[1].replace('x', ' ').replace('+', ' +').replace('-', ' -').split())),
202 'RenderLayer': RenderLayer,
203 'HasAlpha': True,
205 elif Line.startswith('Version'):
206 ResX, ResY = map (int, Line.split()[2].split('x'))
208 #-------------------------------------------------
209 # EXTRACT XCF
210 if OpacityMode == 'BAKE':
211 Opacity = ''
212 else:
213 Opacity = ' --percent 100'
214 for Layer in IMGs:
215 CMD = ('%s -C %s%s -o %s%s.png "%s"%s' %
216 (XCF2PNG, Path, File, PathSave, Layer['LayerName'].replace(' ', '_'), Layer['LayerName'], Opacity))
217 os.system(CMD)
219 #-------------------------------------------------
220 Scene = bpy.context.scene
221 #-------------------------------------------------
222 # CAMERA
224 if SetCamera:
225 bpy.ops.object.camera_add(location=(0, 0, 10))
227 Camera = bpy.context.active_object.data
229 Camera.type = 'ORTHO'
230 Camera.ortho_scale = ResX * .01
232 #-------------------------------------------------
233 # RENDER SETTINGS
235 Render = Scene.render
237 if SetCamera:
238 Render.resolution_x = ResX
239 Render.resolution_y = ResY
240 Render.resolution_percentage = 100
241 Render.alpha_mode = 'TRANSPARENT'
243 #-------------------------------------------------
244 # 3D VIEW SETTINGS
246 Scene.game_settings.material_mode = 'GLSL'
248 Areas = bpy.context.screen.areas
250 for Area in Areas:
251 if Area.type == 'VIEW_3D':
252 Area.spaces.active.viewport_shade = 'TEXTURED'
253 Area.spaces.active.show_textured_solid = True
254 Area.spaces.active.show_floor = False
256 #-------------------------------------------------
257 # 3D LAYERS
259 def Make3DLayer (Name, NameShort, Z, Coords, RenderLayer, LayerMode, LayerOpacity, HasAlpha):
261 # RenderLayer
263 if SetupCompo:
264 if not bpy.context.scene.render.layers.get(RenderLayer):
266 bpy.ops.scene.render_layer_add()
268 LayerActive = bpy.context.scene.render.layers.active
269 LayerActive.name = RenderLayer
270 LayerActive.use_pass_vector = True
271 LayerActive.use_sky = False
272 LayerActive.use_edge_enhance = False
273 LayerActive.use_strand = False
274 LayerActive.use_halo = False
276 global LayerNum
277 for i in range (0,20):
278 if not i == LayerNum:
279 LayerActive.layers[i] = False
281 bpy.context.scene.layers[LayerNum] = True
283 LayerFlags[RenderLayer] = bpy.context.scene.render.layers.active.layers
285 LayerList.append([RenderLayer, LayerMode, LayerOpacity])
287 LayerNum += 1
289 # Object
290 bpy.ops.mesh.primitive_plane_add(view_align=False,
291 enter_editmode=False,
292 rotation=(0, 0, 0))
294 bpy.ops.object.transform_apply(location=False, rotation=True, scale=False)
297 Active = bpy.context.active_object
299 if SetupCompo:
300 Active.layers = LayerFlags[RenderLayer]
302 Active.location = (
303 (float(Coords[2])-(ResX*0.5))*LayerScale,
304 (-float(Coords[3])+(ResY*0.5))*LayerScale, Z)
306 for Vert in Active.data.vertices:
307 Vert.co[0] += 1
308 Vert.co[1] += -1
310 Active.dimensions = float(Coords[0])*LayerScale, float(Coords[1])*LayerScale, 0
312 bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
314 bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', center='MEDIAN')
316 Active.show_wire = True
318 Active.name = NameShort
319 bpy.ops.mesh.uv_texture_add()
321 # Material
323 '''if bpy.data.materials.get(NameShort):
324 Mat = bpy.data.materials[NameShort]
325 if not Active.material_slots:
326 bpy.ops.object.material_slot_add()
327 Active.material_slots[0].material = Mat
328 else:'''
330 Mat = bpy.data.materials.new(NameShort)
331 Mat.diffuse_color = (1,1,1)
332 Mat.use_raytrace = False
333 Mat.use_shadows = False
334 Mat.use_cast_buffer_shadows = False
335 Mat.use_cast_approximate = False
336 if HasAlpha:
337 Mat.use_transparency = True
338 if OpacityMode == 'MAT': Mat.alpha = LayerOpacity
339 else: Mat.alpha = 0
340 if ShadelessMats: Mat.use_shadeless = True
342 if Ext == '.xcf':
343 # Color & Alpha PNG
344 Tex = bpy.data.textures.new(NameShort, 'IMAGE')
345 Tex.extension = 'CLIP'
346 Tex.use_preview_alpha = True
348 Img = bpy.data.images.new(NameShort, 128, 128)
349 Img.source = 'FILE'
350 Img.alpha_mode = AlphaMode
351 Img.filepath = '%s%s%s' % (PathSaveRaw, Name, ExtSave)
353 UVFace = Active.data.uv_textures[0].data[0]
354 UVFace.image = Img
356 Tex.image = Img
358 Mat.texture_slots.add()
359 TexSlot = Mat.texture_slots[0]
360 TexSlot.texture = Tex
361 TexSlot.use_map_alpha = True
362 TexSlot.texture_coords = 'UV'
363 if OpacityMode == 'TEX': TexSlot.alpha_factor = LayerOpacity
364 elif OpacityMode == 'MAT': TexSlot.blend_type = 'MULTIPLY'
366 else: # Ext == '.xjt'
367 # Color JPG
368 Tex = bpy.data.textures.new(NameShort, 'IMAGE')
369 Tex.extension = 'CLIP'
371 Img = bpy.data.images.new(NameShort, 128, 128)
372 Img.source = 'FILE'
373 Img.filepath = '%s%s%s' % (PathSaveRaw, Name, ExtSave)
375 UVFace = Active.data.uv_textures[0].data[0]
376 UVFace.image = Img
378 Tex.image = Img
380 Mat.texture_slots.add()
381 TexSlot = Mat.texture_slots[0]
382 TexSlot.texture = Tex
383 TexSlot.texture_coords = 'UV'
385 if HasAlpha:
386 # Alpha JPG
387 Tex = bpy.data.textures.new(NameShort+'_A', 'IMAGE')
388 Tex.extension = 'CLIP'
389 Tex.use_preview_alpha = True
391 Img = bpy.data.images.new(NameShort+'_A', 128, 128)
392 Img.source = 'FILE'
393 Img.alpha_mode = AlphaMode
394 Img.filepath = '%s%s_A%s' % (PathSaveRaw, Name, ExtSave)
395 Img.use_alpha = False
397 Tex.image = Img
399 Mat.texture_slots.add()
400 TexSlot = Mat.texture_slots[1]
401 TexSlot.texture = Tex
402 TexSlot.use_map_alpha = True
403 TexSlot.use_map_color_diffuse = False
404 TexSlot.texture_coords = 'UV'
405 if OpacityMode == 'TEX': TexSlot.alpha_factor = LayerOpacity
406 elif OpacityMode == 'MAT': TexSlot.blend_type = 'MULTIPLY'
408 if not Active.material_slots:
409 bpy.ops.object.material_slot_add()
411 Active.material_slots[0].material = Mat
414 Z = 0
415 global LayerNum
416 LayerNum = 0
417 LayerFlags = {}
418 LayerList = []
420 for Layer in IMGs:
421 Make3DLayer(Layer['LayerName'].replace(' ', '_'),
422 Layer['LayerNameShort'].replace(' ', '_'),
424 Layer['LayerCoords'],
425 Layer['RenderLayer'],
426 Layer['LayerMode'],
427 Layer['LayerOpacity'],
428 Layer['HasAlpha'],
431 Z -= LayerOffset
433 if SetupCompo:
434 #-------------------------------------------------
435 # COMPO NODES
437 Scene.use_nodes = True
439 Tree = Scene.node_tree
441 for i in Tree.nodes:
442 Tree.nodes.remove(i)
444 LayerList.reverse()
446 Offset = 0
447 LayerLen = len(LayerList)
449 for Layer in LayerList:
451 Offset += 1
453 X_Offset = (500*Offset)
454 Y_Offset = (-300*Offset)
456 Node = Tree.nodes.new('R_LAYERS')
457 Node.location = (-500+X_Offset, 300+Y_Offset)
458 Node.name = 'R_'+ str(Offset)
459 Node.scene = Scene
460 Node.layer = Layer[0]
462 if LayerViewers:
463 Node_V = Tree.nodes.new('VIEWER')
464 Node_V.name = Layer[0]
465 Node_V.location = (-200+X_Offset, 200+Y_Offset)
467 Tree.links.new(Node.outputs[0], Node_V.inputs[0])
469 if LayerLen > Offset:
471 Mode = LayerList[Offset][1] # has to go one step further
472 LayerOpacity = LayerList[Offset][2]
474 if not Mode in {'Normal', '-1'}:
476 Node = Tree.nodes.new('MIX_RGB')
477 if OpacityMode == 'COMPO': Node.inputs['Fac'].default_value[0] = LayerOpacity
478 else: Node.inputs['Fac'].default_value[0] = 1
479 Node.use_alpha = True
481 if Mode in {'Addition', '7'}: Node.blend_type = 'ADD'
482 elif Mode in {'Subtract', '8'}: Node.blend_type = 'SUBTRACT'
483 elif Mode in {'Multiply', '3'}: Node.blend_type = 'MULTIPLY'
484 elif Mode in {'DarkenOnly', '9'}: Node.blend_type = 'DARKEN'
485 elif Mode in {'Dodge', '16'}: Node.blend_type = 'DODGE'
486 elif Mode in {'LightenOnly', '10'}: Node.blend_type = 'LIGHTEN'
487 elif Mode in {'Difference', '6'}: Node.blend_type = 'DIFFERENCE'
488 elif Mode in {'Divide', '15'}: Node.blend_type = 'DIVIDE'
489 elif Mode in {'Overlay', '5'}: Node.blend_type = 'OVERLAY'
490 elif Mode in {'Screen', '4'}: Node.blend_type = 'SCREEN'
491 elif Mode in {'Burn', '17'}: Node.blend_type = 'BURN'
492 elif Mode in {'Color', '13'}: Node.blend_type = 'COLOR'
493 elif Mode in {'Value', '14'}: Node.blend_type = 'VALUE'
494 elif Mode in {'Saturation', '12'}: Node.blend_type = 'SATURATION'
495 elif Mode in {'Hue', '11'}: Node.blend_type = 'HUE'
496 elif Mode in {'Softlight', '19'}: Node.blend_type = 'SOFT_LIGHT'
497 else: pass
499 else:
500 Node = Tree.nodes.new('ALPHAOVER')
501 if OpacityMode == 'COMPO': Node.inputs['Fac'].default_value[0] = LayerOpacity
502 Node.name = 'M_' + str(Offset)
503 Node.location = (300+X_Offset, 250+Y_Offset)
505 if MixerViewers:
506 Node_V = Tree.nodes.new('VIEWER')
507 Node_V.name = Layer[0]
508 Node_V.location = (500+X_Offset, 350+Y_Offset)
510 Tree.links.new(Node.outputs[0], Node_V.inputs[0])
512 else:
513 Node = Tree.nodes.new('COMPOSITE')
514 Node.name = 'Composite'
515 Node.location = (400+X_Offset, 350+Y_Offset)
517 Nodes = bpy.context.scene.node_tree.nodes
519 if LayerLen > 1:
520 for i in range (1, LayerLen + 1):
521 if i == 1:
522 Tree.links.new(Nodes['R_'+str(i)].outputs[0], Nodes['M_'+str(i)].inputs[1])
523 if 1 < i < LayerLen:
524 Tree.links.new(Nodes['M_'+str(i-1)].outputs[0], Nodes['M_'+str(i)].inputs[1])
525 if 1 < i < LayerLen+1:
526 Tree.links.new(Nodes['R_'+str(i)].outputs[0], Nodes['M_'+str(i-1)].inputs[2])
527 if i == LayerLen:
528 Tree.links.new(Nodes['M_'+str(i-1)].outputs[0], Nodes['Composite'].inputs[0])
529 else:
530 Tree.links.new(Nodes['R_1'].outputs[0], Nodes['Composite'].inputs[0])
532 for i in Tree.nodes:
533 i.location[0] += -250*Offset
534 i.location[1] += 150*Offset
536 #------------------------------------------------------------------------
537 import os
538 import bpy
539 from bpy.props import *
540 from math import pi
542 # Operator
543 class GIMPImageToScene(bpy.types.Operator):
544 """"""
545 bl_idname = "import.gimp_image_to_scene"
546 bl_label = "GIMP Image to Scene"
547 bl_description = "Imports GIMP multilayer image files into 3D Scenes"
548 bl_options = {'REGISTER', 'UNDO'}
550 filename = StringProperty(name="File Name",
551 description="Name of the file")
552 directory = StringProperty(name="Directory",
553 description="Directory of the file")
555 LayerViewers = BoolProperty(name="Layer Viewers",
556 description="Add Viewer nodes to each Render Layer node",
557 default=True)
559 MixerViewers = BoolProperty(name="Mixer Viewers",
560 description="Add Viewer nodes to each Mix node",
561 default=True)
563 AlphaMode = EnumProperty(name="Alpha Mode",
564 description="Representation of alpha information in the RGBA pixels",
565 items=(
566 ('STRAIGHT', 'Texture Alpha Factor', 'Transparent RGB and alpha pixels are unmodified'),
567 ('PREMUL', 'Material Alpha Value', 'Transparent RGB pixels are multiplied by the alpha channel')),
568 default='STRAIGHT')
570 ShadelessMats = BoolProperty(name="Shadeless Material",
571 description="Set Materials as Shadeless",
572 default=True)
574 OpacityMode = EnumProperty(name="Opacity Mode",
575 description="Layer Opacity management",
576 items=(
577 ('TEX', 'Texture Alpha Factor', ''),
578 ('MAT', 'Material Alpha Value', ''),
579 ('COMPO', 'Mixer Node Factor', ''),
580 ('BAKE', 'Baked in Image Alpha', '')),
581 default='TEX')
583 SetCamera = BoolProperty(name="Set Camera",
584 description="Create an Ortho Camera matching image resolution",
585 default=True)
587 SetupCompo = BoolProperty(name="Setup Node Compositing",
588 description="Create a compositing node setup (will delete existing nodes)",
589 default=False)
591 GroupUntagged = BoolProperty(name="Group Untagged",
592 description="Layers with no tag go to a single Render Layer",
593 default=False)
595 LayerOffset = FloatProperty(name="Layer Separation",
596 description="Distance between each 3D Layer in the Z axis",
597 min=0,
598 default=0.50)
600 LayerScale = FloatProperty(name="Layer Scale",
601 description="Scale pixel resolution by Blender units",
602 min=0,
603 default=0.01)
605 def draw(self, context):
606 layout = self.layout
608 box = layout.box()
609 box.label('3D Layers:', icon='SORTSIZE')
610 box.prop(self, 'SetCamera', icon='OUTLINER_DATA_CAMERA')
611 box.prop(self, 'OpacityMode', icon='GHOST')
612 if self.OpacityMode == 'COMPO' and self.SetupCompo == False:
613 box.label('Tip: Enable Node Compositing', icon='INFO')
614 box.prop(self, 'AlphaMode', icon='IMAGE_RGB_ALPHA')
615 box.prop(self, 'ShadelessMats', icon='SOLID')
616 box.prop(self, 'LayerOffset')
617 box.prop(self, 'LayerScale')
619 box = layout.box()
620 box.label('Compositing:', icon='RENDERLAYERS')
621 box.prop(self, 'SetupCompo', icon='NODETREE')
622 if self.SetupCompo:
623 box.prop(self, 'GroupUntagged', icon='IMAGE_ZDEPTH')
624 box.prop(self, 'LayerViewers', icon='NODE')
625 box.prop(self, 'MixerViewers', icon='NODE')
627 def execute(self, context):
628 # File Path
629 filename = self.filename
630 directory = self.directory
632 # Settings
633 LayerViewers = self.LayerViewers
634 MixerViewers = self.MixerViewers
635 OpacityMode = self.OpacityMode
636 AlphaMode = self.AlphaMode
637 ShadelessMats = self.ShadelessMats
638 SetCamera = self.SetCamera
639 SetupCompo = self.SetupCompo
640 GroupUntagged = self.GroupUntagged
641 LayerOffset = self.LayerOffset
642 LayerScale = self.LayerScale
644 Ext = None
645 if filename.endswith('.xcf'): Ext = '.xcf'
646 elif filename.endswith('.xjt'): Ext = '.xjt'
648 # Call Main Function
649 if Ext:
650 main(filename, directory, LayerViewers, MixerViewers, LayerOffset,
651 LayerScale, OpacityMode, AlphaMode, ShadelessMats,
652 SetCamera, SetupCompo, GroupUntagged, Ext)
653 else:
654 self.report({'ERROR'},"Selected file wasn't valid, try .xcf or .xjt")
656 return {'FINISHED'}
658 def invoke(self, context, event):
659 wm = bpy.context.window_manager
660 wm.fileselect_add(self)
662 return {'RUNNING_MODAL'}
665 # Registering / Unregister
666 def menu_func(self, context):
667 self.layout.operator(GIMPImageToScene.bl_idname, text="GIMP Image to Scene (.xcf, .xjt)", icon='PLUGIN')
670 def register():
671 bpy.utils.register_module(__name__)
673 bpy.types.INFO_MT_file_import.append(menu_func)
676 def unregister():
677 bpy.utils.unregister_module(__name__)
679 bpy.types.INFO_MT_file_import.remove(menu_func)
682 if __name__ == "__main__":
683 register()