Screencast Keys Addon: Improved mouse silhouette, fixed box width to fit to text...
[blender-addons.git] / io_import_gimp_image_to_scene.py
blob971770a57adc62eb75f1adb24565e63f604034dc
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": "https://developer.blender.org/T25136",
30 "category": "Import-Export"}
32 """
33 This script imports GIMP layered image files into 3D Scenes (.xcf, .xjt)
34 """
36 def main(File, Path, LayerViewers, MixerViewers, LayerOffset,
37 LayerScale, OpacityMode, AlphaMode, ShadelessMats,
38 SetCamera, SetupCompo, GroupUntagged, Ext):
40 #-------------------------------------------------
42 #Folder = '['+File.rstrip(Ext)+']'+'_images/'
43 Folder = 'images_'+'['+File.rstrip(Ext)+']/'
45 if not bpy.data.is_saved:
46 PathSaveRaw = Path+Folder
47 PathSave = PathSaveRaw.replace(' ', '\ ')
48 try: os.mkdir(PathSaveRaw)
49 except: pass
50 else:
51 PathSave = bpy.data.filepath
52 RSlash = PathSave.rfind('/')
53 PathSaveRaw = PathSave[:RSlash+1]+Folder
54 PathSave = PathSaveRaw.replace(' ', '\ ')
55 try: os.mkdir(PathSaveRaw)
56 except: pass
57 PathSaveRaw = bpy.path.relpath(PathSaveRaw)+'/'
59 PathRaw = Path
60 Path = Path.replace(' ', '\ ')
61 if Ext == '.xjt':
62 ExtSave = '.jpg'
63 #-------------------------------------------------
64 # EXTRACT XJT
65 import tarfile
67 IMG = tarfile.open ('%s%s' % (PathRaw, File))
68 PRP = IMG.extractfile('PRP')
70 Members = IMG.getmembers()
72 for Member in Members:
73 Name = Member.name
74 if Name.startswith('l') and Name.endswith('.jpg'):
75 IMG.extract(Name, path=PathSaveRaw)
77 #-------------------------------------------------
78 # INFO XJT
79 IMGs = []
80 for Line in PRP.readlines():
81 Line = str(Line)
83 if Line.startswith("b'GIMP_XJ_IMAGE"):
84 for Segment in Line.split():
85 if Segment.startswith('w/h:'):
86 ResX, ResY = map (int, Segment[4:].split(','))
87 if Line.startswith(("b'L", "b'l")):
89 """The "nice" method to check if layer has alpha channel
90 sadly GIMP sometimes decides not to export an alpha channel
91 if it's pure white so we are not completly sure here yet"""
92 if Line.startswith("b'L"): HasAlpha = True
93 else: HasAlpha = False
95 md = None
96 op = 1
97 ox, oy = 0,0
99 for Segment in Line.split():
101 if Segment.startswith("b'"):
102 imageFile = 'l' + Segment[3:] + '.jpg'
103 imageFileAlpha ='la'+Segment[3:]+'.jpg'
105 """Phisically double checking if alpha image exists
106 now we can be sure! (damn GIMP)"""
107 if HasAlpha:
108 if not os.path.isfile(PathSaveRaw+imageFileAlpha): HasAlpha = False
110 # Get Widht and Height from images
111 data = open(PathSaveRaw+imageFile, "rb").read()
113 hexList = []
114 for ch in data:
115 byt = "%02X" % ch
116 hexList.append(byt)
118 for k in range(len(hexList)-1):
119 if hexList[k] == 'FF' and (hexList[k+1] == 'C0' or hexList[k+1] == 'C2'):
120 ow = int(hexList[k+7],16)*256 + int(hexList[k+8],16)
121 oh = int(hexList[k+5],16)*256 + int(hexList[k+6],16)
123 elif Segment.startswith('md:'): # mode
124 md = Segment[3:]
126 elif Segment.startswith('op:'): # opacity
127 op = float(Segment[3:])*.01
129 elif Segment.startswith('o:'): # origin
130 ox, oy = map(int, Segment[2:].split(','))
132 elif Segment.startswith('n:'): # name
133 n = Segment[3:-4]
134 OpenBracket = n.find ('[')
135 CloseBracket = n.find (']')
137 if OpenBracket != -1 and CloseBracket != -1:
138 RenderLayer = n[OpenBracket+1:CloseBracket]
139 NameShort = n[:OpenBracket]
141 else:
142 RenderLayer = n
143 NameShort = n
145 os.rename(PathSaveRaw+imageFile, PathSaveRaw+NameShort+'.jpg')
146 if HasAlpha: os.rename(PathSaveRaw+imageFileAlpha, PathSaveRaw+NameShort+'_A'+'.jpg')
148 IMGs.append({'LayerMode':md, 'LayerOpacity':op,
149 'LayerName':n, 'LayerNameShort':NameShort,
150 'RenderLayer':RenderLayer, 'LayerCoords':[ow, oh, ox, oy], 'HasAlpha':HasAlpha})
152 else: # Ext == '.xcf':
153 ExtSave = '.png'
154 #-------------------------------------------------
155 # CONFIG
156 XCFInfo = 'xcfinfo'
157 XCF2PNG = 'xcf2png'
158 #-------------------------------------------------
159 # INFO XCF
161 CMD = '%s %s%s' % (XCFInfo, Path, File)
163 Info = os.popen(CMD)
165 IMGs = []
166 for Line in Info.readlines():
167 if Line.startswith ('+'):
169 Line = Line.split(' ', 4)
171 RenderLayer = Line[4]
173 OpenBracket = RenderLayer.find ('[')
174 CloseBracket = RenderLayer.find (']')
176 if OpenBracket != -1 and CloseBracket != -1:
177 RenderLayer = RenderLayer[OpenBracket+1:CloseBracket]
178 NameShort = Line[4][:OpenBracket]
179 else:
180 NameShort = Line[4].rstrip()
181 if GroupUntagged:
182 RenderLayer = '__Undefined__'
183 else:
184 RenderLayer = NameShort
186 LineThree = Line[3]
187 Slash = LineThree.find('/')
188 if Slash == -1:
189 Mode = LineThree
190 Opacity = 1
191 else:
192 Mode = LineThree[:Slash]
193 Opacity = float(LineThree[Slash+1:LineThree.find('%')])*.01
195 IMGs.append ({
196 'LayerMode': Mode,
197 'LayerOpacity': Opacity,
198 'LayerName': Line[4].rstrip(),
199 'LayerNameShort': NameShort,
200 'LayerCoords': list(map(int, Line[1].replace('x', ' ').replace('+', ' +').replace('-', ' -').split())),
201 'RenderLayer': RenderLayer,
202 'HasAlpha': True,
204 elif Line.startswith('Version'):
205 ResX, ResY = map (int, Line.split()[2].split('x'))
207 #-------------------------------------------------
208 # EXTRACT XCF
209 if OpacityMode == 'BAKE':
210 Opacity = ''
211 else:
212 Opacity = ' --percent 100'
213 for Layer in IMGs:
214 CMD = ('%s -C %s%s -o %s%s.png "%s"%s' %
215 (XCF2PNG, Path, File, PathSave, Layer['LayerName'].replace(' ', '_'), Layer['LayerName'], Opacity))
216 os.system(CMD)
218 #-------------------------------------------------
219 Scene = bpy.context.scene
220 #-------------------------------------------------
221 # CAMERA
223 if SetCamera:
224 bpy.ops.object.camera_add(location=(0, 0, 10))
226 Camera = bpy.context.active_object.data
228 Camera.type = 'ORTHO'
229 Camera.ortho_scale = ResX * .01
231 #-------------------------------------------------
232 # RENDER SETTINGS
234 Render = Scene.render
236 if SetCamera:
237 Render.resolution_x = ResX
238 Render.resolution_y = ResY
239 Render.resolution_percentage = 100
240 Render.alpha_mode = 'TRANSPARENT'
242 #-------------------------------------------------
243 # 3D VIEW SETTINGS
245 Scene.game_settings.material_mode = 'GLSL'
247 Areas = bpy.context.screen.areas
249 for Area in Areas:
250 if Area.type == 'VIEW_3D':
251 Area.spaces.active.viewport_shade = 'TEXTURED'
252 Area.spaces.active.show_textured_solid = True
253 Area.spaces.active.show_floor = False
255 #-------------------------------------------------
256 # 3D LAYERS
258 def Make3DLayer (Name, NameShort, Z, Coords, RenderLayer, LayerMode, LayerOpacity, HasAlpha):
260 # RenderLayer
262 if SetupCompo:
263 if not bpy.context.scene.render.layers.get(RenderLayer):
265 bpy.ops.scene.render_layer_add()
267 LayerActive = bpy.context.scene.render.layers.active
268 LayerActive.name = RenderLayer
269 LayerActive.use_pass_vector = True
270 LayerActive.use_sky = False
271 LayerActive.use_edge_enhance = False
272 LayerActive.use_strand = False
273 LayerActive.use_halo = False
275 global LayerNum
276 for i in range (0,20):
277 if not i == LayerNum:
278 LayerActive.layers[i] = False
280 bpy.context.scene.layers[LayerNum] = True
282 LayerFlags[RenderLayer] = bpy.context.scene.render.layers.active.layers
284 LayerList.append([RenderLayer, LayerMode, LayerOpacity])
286 LayerNum += 1
288 # Object
289 bpy.ops.mesh.primitive_plane_add(view_align=False,
290 enter_editmode=False,
291 rotation=(0, 0, 0))
293 bpy.ops.object.transform_apply(location=False, rotation=True, scale=False)
296 Active = bpy.context.active_object
298 if SetupCompo:
299 Active.layers = LayerFlags[RenderLayer]
301 Active.location = (
302 (float(Coords[2])-(ResX*0.5))*LayerScale,
303 (-float(Coords[3])+(ResY*0.5))*LayerScale, Z)
305 for Vert in Active.data.vertices:
306 Vert.co[0] += 1
307 Vert.co[1] += -1
309 Active.dimensions = float(Coords[0])*LayerScale, float(Coords[1])*LayerScale, 0
311 bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
313 bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', center='MEDIAN')
315 Active.show_wire = True
317 Active.name = NameShort
318 bpy.ops.mesh.uv_texture_add()
320 # Material
322 '''if bpy.data.materials.get(NameShort):
323 Mat = bpy.data.materials[NameShort]
324 if not Active.material_slots:
325 bpy.ops.object.material_slot_add()
326 Active.material_slots[0].material = Mat
327 else:'''
329 Mat = bpy.data.materials.new(NameShort)
330 Mat.diffuse_color = (1,1,1)
331 Mat.use_raytrace = False
332 Mat.use_shadows = False
333 Mat.use_cast_buffer_shadows = False
334 Mat.use_cast_approximate = False
335 if HasAlpha:
336 Mat.use_transparency = True
337 if OpacityMode == 'MAT': Mat.alpha = LayerOpacity
338 else: Mat.alpha = 0
339 if ShadelessMats: Mat.use_shadeless = True
341 if Ext == '.xcf':
342 # Color & Alpha PNG
343 Tex = bpy.data.textures.new(NameShort, 'IMAGE')
344 Tex.extension = 'CLIP'
345 Tex.use_preview_alpha = True
347 Img = bpy.data.images.new(NameShort, 128, 128)
348 Img.source = 'FILE'
349 Img.alpha_mode = AlphaMode
350 Img.filepath = '%s%s%s' % (PathSaveRaw, Name, ExtSave)
352 UVFace = Active.data.uv_textures[0].data[0]
353 UVFace.image = Img
355 Tex.image = Img
357 Mat.texture_slots.add()
358 TexSlot = Mat.texture_slots[0]
359 TexSlot.texture = Tex
360 TexSlot.use_map_alpha = True
361 TexSlot.texture_coords = 'UV'
362 if OpacityMode == 'TEX': TexSlot.alpha_factor = LayerOpacity
363 elif OpacityMode == 'MAT': TexSlot.blend_type = 'MULTIPLY'
365 else: # Ext == '.xjt'
366 # Color JPG
367 Tex = bpy.data.textures.new(NameShort, 'IMAGE')
368 Tex.extension = 'CLIP'
370 Img = bpy.data.images.new(NameShort, 128, 128)
371 Img.source = 'FILE'
372 Img.filepath = '%s%s%s' % (PathSaveRaw, Name, ExtSave)
374 UVFace = Active.data.uv_textures[0].data[0]
375 UVFace.image = Img
377 Tex.image = Img
379 Mat.texture_slots.add()
380 TexSlot = Mat.texture_slots[0]
381 TexSlot.texture = Tex
382 TexSlot.texture_coords = 'UV'
384 if HasAlpha:
385 # Alpha JPG
386 Tex = bpy.data.textures.new(NameShort+'_A', 'IMAGE')
387 Tex.extension = 'CLIP'
388 Tex.use_preview_alpha = True
390 Img = bpy.data.images.new(NameShort+'_A', 128, 128)
391 Img.source = 'FILE'
392 Img.alpha_mode = AlphaMode
393 Img.filepath = '%s%s_A%s' % (PathSaveRaw, Name, ExtSave)
394 Img.use_alpha = False
396 Tex.image = Img
398 Mat.texture_slots.add()
399 TexSlot = Mat.texture_slots[1]
400 TexSlot.texture = Tex
401 TexSlot.use_map_alpha = True
402 TexSlot.use_map_color_diffuse = False
403 TexSlot.texture_coords = 'UV'
404 if OpacityMode == 'TEX': TexSlot.alpha_factor = LayerOpacity
405 elif OpacityMode == 'MAT': TexSlot.blend_type = 'MULTIPLY'
407 if not Active.material_slots:
408 bpy.ops.object.material_slot_add()
410 Active.material_slots[0].material = Mat
413 Z = 0
414 global LayerNum
415 LayerNum = 0
416 LayerFlags = {}
417 LayerList = []
419 for Layer in IMGs:
420 Make3DLayer(Layer['LayerName'].replace(' ', '_'),
421 Layer['LayerNameShort'].replace(' ', '_'),
423 Layer['LayerCoords'],
424 Layer['RenderLayer'],
425 Layer['LayerMode'],
426 Layer['LayerOpacity'],
427 Layer['HasAlpha'],
430 Z -= LayerOffset
432 if SetupCompo:
433 #-------------------------------------------------
434 # COMPO NODES
436 Scene.use_nodes = True
438 Tree = Scene.node_tree
440 for i in Tree.nodes:
441 Tree.nodes.remove(i)
443 LayerList.reverse()
445 Offset = 0
446 LayerLen = len(LayerList)
448 for Layer in LayerList:
450 Offset += 1
452 X_Offset = (500*Offset)
453 Y_Offset = (-300*Offset)
455 Node = Tree.nodes.new('R_LAYERS')
456 Node.location = (-500+X_Offset, 300+Y_Offset)
457 Node.name = 'R_'+ str(Offset)
458 Node.scene = Scene
459 Node.layer = Layer[0]
461 if LayerViewers:
462 Node_V = Tree.nodes.new('VIEWER')
463 Node_V.name = Layer[0]
464 Node_V.location = (-200+X_Offset, 200+Y_Offset)
466 Tree.links.new(Node.outputs[0], Node_V.inputs[0])
468 if LayerLen > Offset:
470 Mode = LayerList[Offset][1] # has to go one step further
471 LayerOpacity = LayerList[Offset][2]
473 if not Mode in {'Normal', '-1'}:
475 Node = Tree.nodes.new('MIX_RGB')
476 if OpacityMode == 'COMPO': Node.inputs['Fac'].default_value[0] = LayerOpacity
477 else: Node.inputs['Fac'].default_value[0] = 1
478 Node.use_alpha = True
480 if Mode in {'Addition', '7'}: Node.blend_type = 'ADD'
481 elif Mode in {'Subtract', '8'}: Node.blend_type = 'SUBTRACT'
482 elif Mode in {'Multiply', '3'}: Node.blend_type = 'MULTIPLY'
483 elif Mode in {'DarkenOnly', '9'}: Node.blend_type = 'DARKEN'
484 elif Mode in {'Dodge', '16'}: Node.blend_type = 'DODGE'
485 elif Mode in {'LightenOnly', '10'}: Node.blend_type = 'LIGHTEN'
486 elif Mode in {'Difference', '6'}: Node.blend_type = 'DIFFERENCE'
487 elif Mode in {'Divide', '15'}: Node.blend_type = 'DIVIDE'
488 elif Mode in {'Overlay', '5'}: Node.blend_type = 'OVERLAY'
489 elif Mode in {'Screen', '4'}: Node.blend_type = 'SCREEN'
490 elif Mode in {'Burn', '17'}: Node.blend_type = 'BURN'
491 elif Mode in {'Color', '13'}: Node.blend_type = 'COLOR'
492 elif Mode in {'Value', '14'}: Node.blend_type = 'VALUE'
493 elif Mode in {'Saturation', '12'}: Node.blend_type = 'SATURATION'
494 elif Mode in {'Hue', '11'}: Node.blend_type = 'HUE'
495 elif Mode in {'Softlight', '19'}: Node.blend_type = 'SOFT_LIGHT'
496 else: pass
498 else:
499 Node = Tree.nodes.new('ALPHAOVER')
500 if OpacityMode == 'COMPO': Node.inputs['Fac'].default_value[0] = LayerOpacity
501 Node.name = 'M_' + str(Offset)
502 Node.location = (300+X_Offset, 250+Y_Offset)
504 if MixerViewers:
505 Node_V = Tree.nodes.new('VIEWER')
506 Node_V.name = Layer[0]
507 Node_V.location = (500+X_Offset, 350+Y_Offset)
509 Tree.links.new(Node.outputs[0], Node_V.inputs[0])
511 else:
512 Node = Tree.nodes.new('COMPOSITE')
513 Node.name = 'Composite'
514 Node.location = (400+X_Offset, 350+Y_Offset)
516 Nodes = bpy.context.scene.node_tree.nodes
518 if LayerLen > 1:
519 for i in range (1, LayerLen + 1):
520 if i == 1:
521 Tree.links.new(Nodes['R_'+str(i)].outputs[0], Nodes['M_'+str(i)].inputs[1])
522 if 1 < i < LayerLen:
523 Tree.links.new(Nodes['M_'+str(i-1)].outputs[0], Nodes['M_'+str(i)].inputs[1])
524 if 1 < i < LayerLen+1:
525 Tree.links.new(Nodes['R_'+str(i)].outputs[0], Nodes['M_'+str(i-1)].inputs[2])
526 if i == LayerLen:
527 Tree.links.new(Nodes['M_'+str(i-1)].outputs[0], Nodes['Composite'].inputs[0])
528 else:
529 Tree.links.new(Nodes['R_1'].outputs[0], Nodes['Composite'].inputs[0])
531 for i in Tree.nodes:
532 i.location[0] += -250*Offset
533 i.location[1] += 150*Offset
535 #------------------------------------------------------------------------
536 import os
537 import bpy
538 from bpy.props import *
539 from math import pi
541 # Operator
542 class GIMPImageToScene(bpy.types.Operator):
543 """"""
544 bl_idname = "import.gimp_image_to_scene"
545 bl_label = "GIMP Image to Scene"
546 bl_description = "Imports GIMP multilayer image files into 3D Scenes"
547 bl_options = {'REGISTER', 'UNDO'}
549 filename = StringProperty(name="File Name",
550 description="Name of the file")
551 directory = StringProperty(name="Directory",
552 description="Directory of the file")
554 LayerViewers = BoolProperty(name="Layer Viewers",
555 description="Add Viewer nodes to each Render Layer node",
556 default=True)
558 MixerViewers = BoolProperty(name="Mixer Viewers",
559 description="Add Viewer nodes to each Mix node",
560 default=True)
562 AlphaMode = EnumProperty(name="Alpha Mode",
563 description="Representation of alpha information in the RGBA pixels",
564 items=(
565 ('STRAIGHT', 'Texture Alpha Factor', 'Transparent RGB and alpha pixels are unmodified'),
566 ('PREMUL', 'Material Alpha Value', 'Transparent RGB pixels are multiplied by the alpha channel')),
567 default='STRAIGHT')
569 ShadelessMats = BoolProperty(name="Shadeless Material",
570 description="Set Materials as Shadeless",
571 default=True)
573 OpacityMode = EnumProperty(name="Opacity Mode",
574 description="Layer Opacity management",
575 items=(
576 ('TEX', 'Texture Alpha Factor', ''),
577 ('MAT', 'Material Alpha Value', ''),
578 ('COMPO', 'Mixer Node Factor', ''),
579 ('BAKE', 'Baked in Image Alpha', '')),
580 default='TEX')
582 SetCamera = BoolProperty(name="Set Camera",
583 description="Create an Ortho Camera matching image resolution",
584 default=True)
586 SetupCompo = BoolProperty(name="Setup Node Compositing",
587 description="Create a compositing node setup (will delete existing nodes)",
588 default=False)
590 GroupUntagged = BoolProperty(name="Group Untagged",
591 description="Layers with no tag go to a single Render Layer",
592 default=False)
594 LayerOffset = FloatProperty(name="Layer Separation",
595 description="Distance between each 3D Layer in the Z axis",
596 min=0,
597 default=0.50)
599 LayerScale = FloatProperty(name="Layer Scale",
600 description="Scale pixel resolution by Blender units",
601 min=0,
602 default=0.01)
604 def draw(self, context):
605 layout = self.layout
607 box = layout.box()
608 box.label('3D Layers:', icon='SORTSIZE')
609 box.prop(self, 'SetCamera', icon='OUTLINER_DATA_CAMERA')
610 box.prop(self, 'OpacityMode', icon='GHOST')
611 if self.OpacityMode == 'COMPO' and self.SetupCompo == False:
612 box.label('Tip: Enable Node Compositing', icon='INFO')
613 box.prop(self, 'AlphaMode', icon='IMAGE_RGB_ALPHA')
614 box.prop(self, 'ShadelessMats', icon='SOLID')
615 box.prop(self, 'LayerOffset')
616 box.prop(self, 'LayerScale')
618 box = layout.box()
619 box.label('Compositing:', icon='RENDERLAYERS')
620 box.prop(self, 'SetupCompo', icon='NODETREE')
621 if self.SetupCompo:
622 box.prop(self, 'GroupUntagged', icon='IMAGE_ZDEPTH')
623 box.prop(self, 'LayerViewers', icon='NODE')
624 box.prop(self, 'MixerViewers', icon='NODE')
626 def execute(self, context):
627 # File Path
628 filename = self.filename
629 directory = self.directory
631 # Settings
632 LayerViewers = self.LayerViewers
633 MixerViewers = self.MixerViewers
634 OpacityMode = self.OpacityMode
635 AlphaMode = self.AlphaMode
636 ShadelessMats = self.ShadelessMats
637 SetCamera = self.SetCamera
638 SetupCompo = self.SetupCompo
639 GroupUntagged = self.GroupUntagged
640 LayerOffset = self.LayerOffset
641 LayerScale = self.LayerScale
643 Ext = None
644 if filename.endswith('.xcf'): Ext = '.xcf'
645 elif filename.endswith('.xjt'): Ext = '.xjt'
647 # Call Main Function
648 if Ext:
649 main(filename, directory, LayerViewers, MixerViewers, LayerOffset,
650 LayerScale, OpacityMode, AlphaMode, ShadelessMats,
651 SetCamera, SetupCompo, GroupUntagged, Ext)
652 else:
653 self.report({'ERROR'},"Selected file wasn't valid, try .xcf or .xjt")
655 return {'FINISHED'}
657 def invoke(self, context, event):
658 wm = bpy.context.window_manager
659 wm.fileselect_add(self)
661 return {'RUNNING_MODAL'}
664 # Registering / Unregister
665 def menu_func(self, context):
666 self.layout.operator(GIMPImageToScene.bl_idname, text="GIMP Image to Scene (.xcf, .xjt)", icon='PLUGIN')
669 def register():
670 bpy.utils.register_module(__name__)
672 bpy.types.INFO_MT_file_import.append(menu_func)
675 def unregister():
676 bpy.utils.unregister_module(__name__)
678 bpy.types.INFO_MT_file_import.remove(menu_func)
681 if __name__ == "__main__":
682 register()