Package fife :: Package extensions :: Package serializers :: Module xmlmap
[hide private]
[frames] | no frames]

Source Code for Module fife.extensions.serializers.xmlmap

  1  # -*- coding: utf-8 -*-
 
  2  # ####################################################################
 
  3  #  Copyright (C) 2005-2010 by the FIFE team
 
  4  #  http://www.fifengine.de
 
  5  #  This file is part of FIFE.
 
  6  #
 
  7  #  FIFE is free software; you can redistribute it and/or
 
  8  #  modify it under the terms of the GNU Lesser General Public
 
  9  #  License as published by the Free Software Foundation; either
 
 10  #  version 2.1 of the License, or (at your option) any later version.
 
 11  #
 
 12  #  This library is distributed in the hope that it will be useful,
 
 13  #  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 14  #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
 15  #  Lesser General Public License for more details.
 
 16  #
 
 17  #  You should have received a copy of the GNU Lesser General Public
 
 18  #  License along with this library; if not, write to the
 
 19  #  Free Software Foundation, Inc.,
 
 20  #  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
 21  # ####################################################################
 
 22  
 
 23  """ main xml parser class for xml map loading """ 
 24  
 
 25  import time 
 26  
 
 27  from fife import fife 
 28  
 
 29  from fife.extensions.serializers import ET 
 30  from fife.extensions.serializers import SerializerError, InvalidFormat  
 31  from fife.extensions.serializers import NameClash, NotFound, WrongFileType 
 32  
 
 33  from fife.extensions.serializers.xmlobject import XMLObjectLoader 
 34  from fife.extensions.serializers.xmlanimation import loadXMLAnimation 
 35  from fife.extensions.serializers.xml_loader_tools import loadImportFile, loadImportDir 
 36  from fife.extensions.serializers.xml_loader_tools import loadImportDirRec 
 37  from fife.extensions.serializers.xml_loader_tools import root_subfile, reverse_root_subfile      
 38          
 
 39  
 
 40  FORMAT = '1.0' 
 41  
 
42 -class XMLMapLoader(object):
43 """ The B{XMLMapLoader} parses the xml map using several section. 44 Each section fires a callback (if given) which can e. g. be 45 used to show a progress bar. 46 47 The callback sends two values, a string and a float (which shows 48 the overall process): callback(string, float) 49 """
50 - def __init__(self, engine, callback, debug, extensions):
51 """ 52 @type engine: object 53 @param engine: a pointer to fife.engine 54 @type callback: function 55 @param callback: a callback with two arguments, optional 56 @type debug: bool 57 @param debug: flag to activate / deactivate print statements 58 @type extensions: dict 59 @param extensions: information package which extension should be activated (lights, sounds) 60 """ 61 # self.thisown = 0 62 63 self.callback = callback 64 self.debug = debug 65 66 self.engine = engine 67 self.vfs = self.engine.getVFS() 68 self.model = self.engine.getModel() 69 self.image_manager = self.engine.getImageManager() 70 self.anim_pool = None 71 72 self.obj_loader = XMLObjectLoader(engine) 73 74 self.map = None 75 self.source = None 76 self.time_to_load = 0 77 78 self.nspace = None 79 80 self.msg = {} 81 self.msg['map'] = 'created map' 82 self.msg['imports'] = 'loaded imports' 83 self.msg['layer'] = 'loaded layer: %s' 84 self.msg['camera'] = 'loaded camera: %s' 85 86 if 'sound' not in extensions: 87 extensions['sound'] = False 88 if 'lights' not in extensions: 89 extensions['lights'] = False 90 91 self.light_data = {} 92 self.extensions = extensions
93
94 - def _err(self, msg):
95 raise SyntaxError(''.join(['File: ', self.source, ' . ', msg]))
96
97 - def loadResource(self, location):
98 """ overwrite of B{fife.ResourceLoader} 99 100 @type location: object 101 @param location: path to a map file as a fife.ResourceLocation 102 @type map: object 103 @return map: FIFE map object 104 """ 105 start_time = time.time() 106 self.source = location 107 f = self.vfs.open(self.source) 108 f.thisown = 1 109 tree = ET.parse(f) 110 root = tree.getroot() 111 112 map = self.parse_map(root) 113 self.time_to_load = time.time() - start_time 114 return map
115
116 - def parse_map(self, mapelt):
117 """ start parsing the xml structure and 118 call submethods for turning found tags 119 into FIFE objects and create the map 120 121 @type mapelt: object 122 @param mapelt: ElementTree root 123 @type map: object 124 @return map: FIFE map object 125 """ 126 if not mapelt: 127 self._err('No <map> element found at top level of map file definition.') 128 _id, format = mapelt.get('id'), mapelt.get('format') 129 130 if not format == FORMAT: self._err(''.join(['This file has format ', format, ' but this loader has format ', FORMAT])) 131 if not _id: self._err('Map declared without an identifier.') 132 133 map = None 134 try: 135 self.map = self.model.createMap(str(_id)) 136 self.map.setFilename(self.source) 137 except fife.Exception, e: # NameClash appears as general fife.Exception; any ideas? 138 print e.getMessage() 139 print ''.join(['File: ', self.source, '. The map ', str(_id), ' already exists! Ignoring map definition.']) 140 return map 141 142 # xml-specific directory imports. This is used by xml savers. 143 self.map.importDirs = [] 144 145 if self.callback is not None: 146 self.callback(self.msg['map'], float(0.25) ) 147 148 self.parse_imports(mapelt, self.map) 149 self.parse_layers(mapelt, self.map) 150 self.parse_cameras(mapelt, self.map) 151 152 # create light nodes 153 if self.light_data: 154 self.create_light_nodes(self.map) 155 156 return self.map
157
158 - def parse_imports(self, mapelt, map):
159 """ load all objects defined as import into memory 160 161 @type mapelt: object 162 @param mapelt: ElementTree root 163 @type map: object 164 @map map: FIFE map object 165 """ 166 parsedImports = {} 167 168 if self.callback: 169 tmplist = mapelt.findall('import') 170 i = float(0) 171 172 for item in mapelt.findall('import'): 173 _file = item.get('file') 174 if _file: 175 _file = reverse_root_subfile(self.source, _file) 176 _dir = item.get('dir') 177 if _dir: 178 _dir = reverse_root_subfile(self.source, _dir) 179 180 # Don't parse duplicate imports 181 if (_dir,_file) in parsedImports: 182 if self.debug: print "Duplicate import:" ,(_dir, _file) 183 continue 184 parsedImports[(_dir,_file)] = 1 185 186 if _file and _dir: 187 loadImportFile(self.obj_loader, '/'.join(_dir, _file), self.engine, self.debug) 188 elif _file: 189 loadImportFile(self.obj_loader, _file, self.engine, self.debug) 190 elif _dir: 191 loadImportDirRec(self.obj_loader, _dir, self.engine, self.debug) 192 map.importDirs.append(_dir) 193 else: 194 if self.debug: print 'Empty import statement?' 195 196 if self.callback: 197 i += 1 198 self.callback(self.msg['imports'], float( i / float(len(tmplist)) * 0.25 + 0.25 ) )
199
200 - def parse_layers(self, mapelt, map):
201 """ create all layers and their instances 202 203 @type mapelt: object 204 @param mapelt: ElementTree root 205 @type map: object 206 @map map: FIFE map object 207 """ 208 if self.callback is not None: 209 tmplist = mapelt.findall('layer') 210 i = float(0) 211 212 for layer in mapelt.findall('layer'): 213 _id = layer.get('id') 214 grid_type = layer.get('grid_type') 215 216 if not _id: self._err('<layer> declared with no id attribute.') 217 if not grid_type: self._err(''.join(['Layer ', str(_id), ' has no grid_type attribute.'])) 218 219 x_scale = layer.get('x_scale') 220 y_scale = layer.get('y_scale') 221 rotation = layer.get('rotation') 222 x_offset = layer.get('x_offset') 223 y_offset = layer.get('y_offset') 224 z_offset = layer.get('z_offset') 225 pathing = layer.get('pathing') 226 transparency = layer.get('transparency') 227 228 if not x_scale: x_scale = 1.0 229 if not y_scale: y_scale = 1.0 230 if not rotation: rotation = 0.0 231 if not x_offset: x_offset = 0.0 232 if not y_offset: y_offset = 0.0 233 if not z_offset: z_offset = 0.0 234 if not pathing: pathing = "cell_edges_only" 235 if not transparency: 236 transparency = 0 237 else: 238 transparency = int(transparency) 239 240 cellgrid = self.model.getCellGrid(grid_type) 241 if not cellgrid: self._err('<layer> declared with invalid cellgrid type. (%s)' % grid_type) 242 243 cellgrid.setRotation(float(rotation)) 244 cellgrid.setXScale(float(x_scale)) 245 cellgrid.setYScale(float(y_scale)) 246 cellgrid.setXShift(float(x_offset)) 247 cellgrid.setYShift(float(y_offset)) 248 cellgrid.setZShift(float(z_offset)) 249 cellgrid.setAllowDiagonals(pathing != "cell_edges_only"); 250 251 layer_obj = None 252 try: 253 layer_obj = map.createLayer(str(_id), cellgrid) 254 except fife.Exception, e: 255 print e.getMessage() 256 print 'The layer ' + str(_id) + ' already exists! Ignoring this layer.' 257 258 continue 259 260 strgy = fife.CELL_EDGES_ONLY 261 if pathing == "cell_edges_and_diagonals": 262 strgy = fife.CELL_EDGES_AND_DIAGONALS 263 if pathing == "freeform": 264 strgy = fife.FREEFORM 265 266 layer_obj.setPathingStrategy(strgy) 267 layer_obj.setLayerTransparency(transparency) 268 269 self.parse_instances(layer, layer_obj) 270 271 if self.extensions['lights']: 272 self.parse_lights(layer, layer_obj) 273 if self.extensions['sound']: 274 self.parse_sounds(layer, layer_obj) 275 276 if self.callback is not None: 277 i += 1 278 self.callback(self.msg['layer'] % str(_id), float( i / float(len(tmplist)) * 0.25 + 0.5 ) ) 279 280 # cleanup 281 if self.callback is not None: 282 del tmplist 283 del i
284
285 - def parse_lights(self, layerelt, layer):
286 """ create light nodes 287 288 @type layerelt: object 289 @param layerelt: ElementTree layer branch 290 @type layer: object 291 @map layer: FIFE layer object 292 """ 293 _LIGHT_DEFAULT_BLENDING_SRC = -1 294 _LIGHT_DEFAULT_BLENDING_DST = -1 295 _LIGHT_DEFAULT_SUBDIVISIONS = 32 296 _LIGHT_DEFAULT_CAM_ID = 'default' 297 _LIGHT_DEFAULT_INTENSITY = 128 298 _LIGHT_DEFAULT_RADIUS = 10.0 299 300 print "Processing lights ... " 301 lightelt = layerelt.find('lights') 302 if not lightelt: 303 print "\tno lights found on layer %s" % layer.getId() 304 return 305 306 lights = [] 307 for attr in ('l', 'light', 'lgt'): 308 lights.extend(lightelt.findall(attr)) 309 310 for light in lights: 311 group = light.get('group') 312 if not group: 313 print "Light has no group. Omitting..." 314 continue 315 316 blending_src = light.get('src') 317 if not blending_src: 318 blending_src = _LIGHT_DEFAULT_BLENDING_SRC 319 blending_dst = light.get('dst') 320 if not blending_dst: 321 blending_dst = _LIGHT_DEFAULT_BLENDING_DST 322 323 _x = light.get('x') 324 if not _x: _x = 0 325 _y = light.get('y') 326 if not _y: _y = 0 327 _z = light.get('y') 328 if not _z: _z = 0 329 330 node = {} 331 node['blending_src'] = int(blending_src) 332 node['blending_dst'] = int(blending_dst) 333 node['layer'] = layer.getId() 334 node['position'] = int(_x), int(_y), int(_z) 335 336 # where is the light? *sing* 337 instance_id = light.get('instance') 338 node['instance'] = None 339 if instance_id and layer.getInstance(instance_id): 340 node['instance'] = instance_id 341 342 type = light.get('type') 343 if type: 344 s_ref = light.get('s_ref') 345 if not s_ref: s_ref = -1 346 node['s_ref'] = int(s_ref) 347 a_ref = light.get('a_ref') 348 if not a_ref: a_ref = 0.0 349 node['a_ref'] = float(a_ref) 350 351 if type == 'image': 352 image = light.get('image') 353 if not image: 354 print "Light has no image. Omitting..." 355 continue 356 node['type'] = 'image' 357 image = reverse_root_subfile(self.source, image) 358 img = self.image_manager.create(image) 359 node['image'] = img 360 elif type == 'animation': 361 animation = light.get('animation') 362 if not animation: 363 print "Light has no animation. Omitting..." 364 continue 365 node['type'] = 'animation' 366 animation = reverse_root_subfile(self.source, animation) 367 anim = loadXMLAnimation(self.engine, animation) 368 node['animation'] = anim 369 elif type == 'simple': 370 node['type'] = type 371 radius = light.get('radius') 372 if not radius: radius = _LIGHT_DEFAULT_RADIUS 373 node['radius'] = float(radius) 374 375 subdivisions = light.get('subdivisions') 376 if not subdivisions: 377 subdivisions = _LIGHT_DEFAULT_SUBDIVISIONS 378 node['subdivisions'] = int(subdivisions) 379 380 intensity = light.get('intensity') 381 if not intensity: 382 intensity = _LIGHT_DEFAULT_INTENSITY 383 node['intensity'] = int(intensity) 384 385 xstretch = light.get('xstretch') 386 if not xstretch: xstretch = 1.0 387 ystretch = light.get('ystretch') 388 if not ystretch: ystretch = 1.0 389 node['stretch'] = float(xstretch), float(ystretch) 390 391 color = light.get('color') 392 if not color: color = '%d,%d,%d' % (255, 255, 255) 393 node['color'] = ([int(c) for c in color.split(',')]) 394 395 else: 396 continue 397 398 cam_id = light.get('camera_id') 399 if not cam_id: cam_id = _LIGHT_DEFAULT_CAM_ID 400 401 if not cam_id in self.light_data: 402 self.light_data[cam_id] = {} 403 if group not in self.light_data[cam_id]: 404 self.light_data[cam_id][group] = [] 405 406 self.light_data[cam_id][group].append(node) 407 408 for camera, groups in self.light_data.iteritems(): 409 print "Lights for camera %s" % camera 410 for group, lights in groups.iteritems(): 411 print group, lights
412
413 - def parse_sounds(self, layerelt, layer):
414 """ create sound emitter 415 416 FIXME: 417 - FIFE has a hard limit of sound emitters 418 how should we load emitters here? 419 - my first thought: collect a list of sound 420 files & data for emitter creation, 421 then let the client decide what to do with it 422 423 @type layerelt: object 424 @param layerelt: ElementTree layer branch 425 @type layer: object 426 @map layer: FIFE layer object 427 """ 428 # to be continued 429 pass
430
431 - def parse_instances(self, layerelt, layer):
432 """ create all layers and their instances 433 434 @type layerelt: object 435 @param layerelt: ElementTree layer branch 436 @type layer: object 437 @map layer: FIFE layer object 438 """ 439 instelt = layerelt.find('instances') 440 441 instances = [] 442 for attr in ('i', 'inst', 'instance'): 443 instances.extend(instelt.findall(attr)) 444 445 for instance in instances: 446 _id = instance.get('id') 447 if not _id: 448 _id = '' 449 450 objectID = '' 451 for attr in ('o', 'object', 'obj'): 452 objectID = instance.get(attr) 453 if objectID: break 454 if not objectID: self._err('<instance> %s does not specify an object attribute.' % str(objectID)) 455 objectID = str(objectID) 456 457 nspace = '' 458 for attr in ('namespace', 'ns'): 459 nspace = instance.get(attr) 460 if nspace: break 461 # try to reuse the previous namespace 462 if not nspace and self.nspace: 463 nspace = self.nspace 464 if not nspace and not self.nspace: self._err('<instance> %s does not specify an object namespace, and no default is available.' % str(objectID)) 465 nspace = str(nspace) 466 self.nspace = nspace 467 468 # check if there is an object for this instance available, if not -> skip this one 469 object = self.model.getObject(objectID, nspace) 470 if not object: 471 print "Object with id=%s, ns=%s could not be found. Omitting..." % (objectID, nspace) 472 continue 473 474 x = instance.get('x') 475 if x: self.x = x = float(x) 476 else: x = self.x 477 478 y = instance.get('y') 479 if y: self.y = y = float(y) 480 else: y = self.y 481 482 z = instance.get('z') 483 if z: z = float(z) 484 else: z = 0.0 485 486 inst = layer.createInstance(object, fife.ExactModelCoordinate(x,y,z), _id) 487 488 rotation = 0 489 for attr in ('r', 'rotation'): 490 rotation = instance.get(attr) 491 if rotation: break 492 if not rotation: 493 angles = object.get2dGfxVisual().getStaticImageAngles() 494 if angles: 495 rotation = angles[0] 496 else: 497 rotation = 0 498 else: 499 rotation = int(rotation) 500 inst.setRotation(rotation) 501 502 over_block = instance.get('override_blocking') 503 if over_block is not None: 504 inst.setOverrideBlocking(bool(over_block)) 505 blocking = instance.get('blocking') 506 if blocking is not None: 507 inst.setBlocking(bool(int(blocking))) 508 509 fife.InstanceVisual.create(inst) 510 511 stackpos = instance.get('stackpos') 512 if stackpos: 513 inst.get2dGfxVisual().setStackPosition(int(stackpos)) 514 515 if (object.getAction('default')): 516 target = fife.Location(layer) 517 inst.act('default', target, True)
518
519 - def parse_cameras(self, mapelt, map):
520 """ create all cameras and activate them 521 522 FIXME: 523 - should the cameras really be enabled here? 524 IMO that's part of the setup within a client 525 (we just _load_ things here) 526 527 @type mapelt: object 528 @param mapelt: ElementTree root 529 @type map: object 530 @map map: FIFE map object 531 """ 532 if self.callback: 533 tmplist = mapelt.findall('camera') 534 i = float(0) 535 536 for camera in mapelt.findall('camera'): 537 _id = camera.get('id') 538 zoom = camera.get('zoom') 539 tilt = camera.get('tilt') 540 rotation = camera.get('rotation') 541 ref_layer_id = camera.get('ref_layer_id') 542 ref_cell_width = camera.get('ref_cell_width') 543 ref_cell_height = camera.get('ref_cell_height') 544 viewport = camera.get('viewport') 545 light_color = camera.get('light_color') 546 547 if not zoom: zoom = 1 548 if not tilt: tilt = 0 549 if not rotation: rotation = 0 550 551 if not _id: self._err('Camera declared without an id.') 552 if not ref_layer_id: self._err(''.join(['Camera ', str(_id), ' declared with no reference layer.'])) 553 if not (ref_cell_width and ref_cell_height): self._err(''.join(['Camera ', str(_id), ' declared without reference cell dimensions.'])) 554 555 try: 556 if viewport: 557 cam = map.addCamera(str(_id), map.getLayer(str(ref_layer_id)),fife.Rect(*[int(c) for c in viewport.split(',')])) 558 559 else: 560 screen = self.engine.getRenderBackend() 561 cam = map.addCamera(str(_id), map.getLayer(str(ref_layer_id)),fife.Rect(0,0,screen.getScreenWidth(),screen.getScreenHeight())) 562 563 renderer = fife.InstanceRenderer.getInstance(cam) 564 renderer.activateAllLayers(map) 565 566 except fife.Exception, e: 567 print e.getMessage() 568 569 if light_color: cam.setLightingColor(*[float(c) for c in light_color.split(',')]) 570 cam.setCellImageDimensions(int(ref_cell_width), int(ref_cell_height)) 571 cam.setRotation(float(rotation)) 572 cam.setTilt(float(tilt)) 573 cam.setZoom(float(zoom)) 574 575 renderer = fife.InstanceRenderer.getInstance(cam) 576 renderer.activateAllLayers(map) 577 578 if self.callback: 579 i += 1 580 self.callback(self.msg['camera'] % str(_id), float( i / len(tmplist) * 0.25 + 0.75 ) )
581
582 - def create_light_nodes(self, map):
583 """ loop through all preloaded lights and create them 584 according to their data 585 586 @type map: object 587 @param map: FIFE map object 588 """ 589 cameras = [i.getId() for i in map.getCameras()] 590 renderers = {} 591 default_cam = map.getCameras()[0].getId() 592 593 def add_simple_light(group, renderer, node, data): 594 """ add a node as simple light to the renderer 595 596 @type group: string 597 @param group: name of the light group 598 @type renderer: object 599 @param renderer: fife.LightRenderer instance 600 @type node: object 601 @param node: fife.RendererNode instance 602 @type data: dict 603 @param data: all data for the light type creation 604 """ 605 if not node: return 606 if not group: return 607 renderer.addSimpleLight( 608 group, 609 node, 610 data['intensity'], 611 data['radius'], 612 data['subdivisions'], 613 data['stretch'][0], 614 data['stretch'][1], 615 data['color'][0], 616 data['color'][1], 617 data['color'][2], 618 data['blending_src'], 619 data['blending_dst'], 620 ) 621 if data['s_ref'] is not -1: 622 add_stencil_test(group, renderer, data)
623 def add_animated_lightmap(group, renderer, node, data): 624 """ add a node as animated lightmap to the renderer 625 626 @type group: string 627 @param group: name of the light group 628 @type renderer: object 629 @param renderer: fife.LightRenderer instance 630 @type node: object 631 @param node: fife.RendererNode instance 632 @type data: dict 633 @param data: all data for the light type creation 634 """ 635 if not node: return 636 if not group: return 637 renderer.addAnimation( 638 group, 639 node, 640 data['animation'], 641 data['blending_src'], 642 data['blending_dst'], 643 ) 644 if data['s_ref'] is not -1: 645 add_stencil_test(group, renderer, data)
646 def add_lightmap(group, renderer, node, data): 647 """ add a node as lightmap to the renderer 648 649 @type group: string 650 @param group: name of the light group 651 @type renderer: object 652 @param renderer: fife.LightRenderer instance 653 @type node: object 654 @param node: fife.RendererNode instance 655 @type data: dict 656 @param data: all data for the light type creation 657 """ 658 if not node: return 659 if not group: return 660 renderer.addImage( 661 group, 662 node, 663 data['image'], 664 data['blending_src'], 665 data['blending_dst'], 666 ) 667 if data['s_ref'] is not -1: 668 add_stencil_test(group, renderer, data) 669 def add_stencil_test(group, renderer, data): 670 """ add a stencil test to a group 671 672 @type group: string 673 @param group: name of the light group 674 @type renderer: object 675 @param renderer: fife.LightRenderer instance 676 @type data: dict 677 @param data: all data for the light type creation 678 """ 679 if not group: return 680 renderer.addStencilTest( 681 group, 682 data['s_ref'], 683 data['a_ref'], 684 ) 685 686 def create_node(instance=None, point=None, layer=None): 687 """ creates a node of one of these types: 688 689 - attached to an instance 690 - attached to an instance with offset 691 - attached to a point 692 693 FIXME: 694 - add location node 695 696 @type: instance: object 697 @param instance: fife instance object 698 @type point: tuple 699 @param point: x,y,z tuple 700 @type layer: object 701 @param layer: fife layer object 702 """ 703 node = None 704 if not layer: return node 705 706 # node at layer coordinates 707 if point and not instance: 708 point = fife.Point(point[0], point[1]) 709 node = fife.RendererNode(point); 710 # node with offset 711 if instance and point: 712 node = fife.RendererNode(instance, layer, fife.Point(point[0], point[1])) 713 # node attached to instance 714 if instance and not point: 715 node = fife.RendererNode(instance, layer) 716 717 if node: 718 node.thisown = 0 719 720 return node 721 722 def dump_data(): 723 """ dump all loaded data """ 724 for camera, groups in self.light_data.iteritems(): 725 print "Lights for camera %s" % camera 726 for group, lights in groups.iteritems(): 727 print group, lights 728 729 # fetch all renderer instances for available cameras 730 for _id in cameras: 731 camera = map.getCamera(_id) 732 renderers[_id] = fife.LightRenderer.getInstance(camera) 733 734 # parse data and create the lights 735 for camera, groups in self.light_data.iteritems(): 736 for group, lights in groups.iteritems(): 737 for light in lights: 738 instance = None 739 layer = map.getLayer(light['layer']) 740 if light['instance']: 741 instance = layer.getInstance(light['instance']) 742 position = light['position'] 743 node = create_node(instance, position, layer) 744 745 # some guards 746 if not node: continue 747 748 if camera == 'default': 749 renderer = renderers[default_cam] 750 else: 751 renderer = renderers[camera] 752 753 if light['type'] == 'simple': 754 add_simple_light(group, renderer, node, light) 755 elif light['type'] == 'image': 756 add_lightmap(group, renderer, node, light) 757 elif light['type'] == 'animation': 758 add_animated_lightmap(group, renderer, node, light) 759 760 # dump_data() 761