22 * patched to work with 2.66. 24 * apply patch from Thomas for Blender 2.6x support 26 * Clean all names that will be used as filenames on disk. Adjust all places 27 that use these names for refs instead of ob.name/ob.data.name. Replaced chars 28 are \, /, :, *, ?, ", <, >, | and spaces. Tested on work with ogre 29 material, mesh and skeleton writing/refs inside the files and txml refs. 30 Shows warning at final report if we had to resort to the renaming so user 31 can possibly rename the object. 32 * Added silent auto update checks if blender2ogre was installed using 33 the .exe installer. This will keep people up to date when new versions are out. 34 * Fix tracker issue 48: Needs to check if outputting to /tmp or 35 ~/.wine/drive_c/tmp on Linux. Thanks to vax456 for providing the patch, 36 added him to contributors. Preview mesh's are now placed under /tmp 37 on Linux systems if the OgreMeshy executable ends with .exe 38 * Fix tracker issue 46: add operationtype to <submesh> 39 * Implement a modal dialog that reports if material names have invalid 40 characters and cant be saved on disk. This small popup will show until 41 user presses left or right mouse (anywhere). 42 * Fix tracker issue 44: XML Attributes not properly escaped in .scene file 43 * Implemented reading OgreXmlConverter path from windows registry. 44 The .exe installer will ship with certain tools so we can stop guessing 45 and making the user install tools separately and setting up paths. 46 * Fix bug that .mesh files were not generated while doing a .txml export. 47 This was result of the late 2.63 mods that forgot to update object 48 facecount before determining if mesh should be exported. 49 * Fix bug that changed settings in the export dialog were forgotten when you 50 re-exported without closing blender. Now settings should persist always 51 from the last export. They are also stored to disk so the same settings 52 are in use when if you restart Blender. 53 * Fix bug that once you did a export, the next time the export location was 54 forgotten. Now on sequential exports, the last export path is remembered in 56 * Remove all local:// from asset refs and make them relative in .txml export. 57 Having relative refs is the best for local preview and importing the txml 59 * Make .material generate what version of this plugins was used to generate 60 the material file. Will be helpful in production to catch things. 61 Added pretty printing line endings so the raw .material data is easier to read. 62 * Improve console logging for the export stages. Run Blender from 63 cmd prompt to see this information. 64 * Clean/fix documentation in code for future development 65 * Add todo to code for future development 66 * Restructure/move code for easier readability 67 * Remove extra white spaces and convert tabs to space 69 * Update to Blender 2.6.3. 70 * Fixed xz-y Skeleton rotation (again) 71 * Added additional Keyframe at the end of each animation to prevent 72 ogre from interpolating back to the start 73 * Added option to ignore non-deformable bones 74 * Added option to export nla-strips independently from each other 77 * Remove this section and integrate below with code :) 78 * Fix terrain collision offset bug 79 * Add realtime transform (rotation is missing) 80 * Fix camera rotated -90 ogre-dot-scene 81 * Set description field for all pyRNA 85 "name":
"OGRE Exporter (.scene, .mesh, .skeleton) and RealXtend (.txml)",
86 "author":
"Brett, S.Rombauts, F00bar, Waruck, Mind Calamity, Mr.Magne, Jonne Nauha, vax456",
89 "location":
"File > Export...",
90 "description":
"Export to Ogre xml and binary formats",
91 "wiki_url":
"http://code.google.com/p/blender2ogre/w/list",
92 "tracker_url":
"http://code.google.com/p/blender2ogre/issues/list",
93 "category":
"Import-Export" 102 ''' Toggles the Ogre interface panels ''' 103 if cls
not in UI_CLASSES:
104 UI_CLASSES.append(cls)
108 for cls
in UI_CLASSES:
109 bpy.utils.unregister_class( cls )
115 for o
in bpy.data.objects:
116 if o.uid > high: high = o.uid
117 if o.use_multires_lod: multires += 1
118 high += 1 + (multires*10)
119 if high < 100: high = 100
125 import os, sys, time, array, ctypes, math
129 import bpy, mathutils
133 assert __name__ ==
'__main__' 134 print(
'Trying to compile Rpython C-library')
135 assert sys.version_info.major == 2
136 print(
'...searching for rpythonic...')
137 sys.path.append(
'../rpythonic')
139 rpythonic.set_pypy_root(
'../pypy' )
140 import pypy.rpython.lltypesystem.rffi
as rffi
141 from pypy.rlib
import streamio
142 rpy = rpythonic.RPython(
'blender2ogre' )
156 def dotmesh( path, facesAddr, facesSmoothAddr, facesMatAddr, vertsPosAddr, vertsNorAddr, numFaces, numVerts, materialNames ):
157 print(
'PATH----------------', path)
159 for matname
in materialNames.split(
';'):
160 print(
'Material Name: %s' %matname )
161 materials.append( matname )
163 file = streamio.open_file_as_stream( path,
'w')
165 faces = rffi.cast( rffi.UINTP, facesAddr )
166 facesSmooth = rffi.cast( rffi.CCHARP, facesSmoothAddr )
167 facesMat = rffi.cast( rffi.USHORTP, facesMatAddr )
169 vertsPos = rffi.cast( rffi.FLOATP, vertsPosAddr )
170 vertsNor = rffi.cast( rffi.FLOATP, vertsNorAddr )
174 '<vertexbuffer positions="true" normals="true">' 179 for fidx
in range( numFaces ):
180 smooth = ord( facesSmooth[ fidx ] )
182 matidx = facesMat[ fidx ]
184 ai = faces[ i ]; bi = faces[ i+1 ]
185 ci = faces[ i+2 ]; di = faces[ i+3 ]
188 for J
in [ai, bi, ci]:
190 x = rffi.cast( rffi.DOUBLE, vertsPos[ i ] )
191 y = rffi.cast( rffi.DOUBLE, vertsPos[ i+1 ] )
192 z = rffi.cast( rffi.DOUBLE, vertsPos[ i+2 ] )
195 x = rffi.cast( rffi.DOUBLE, vertsNor[ i ] )
196 y = rffi.cast( rffi.DOUBLE, vertsNor[ i+1 ] )
197 z = rffi.cast( rffi.DOUBLE, vertsNor[ i+2 ] )
203 for otherSIG
in fastlookup[ J ]:
205 triangle.append( fastlookup[J][otherSIG] )
210 triangle.append( ogre_vert_index )
211 fastlookup[ J ][ SIG ] = ogre_vert_index
214 triangle.append( ogre_vert_index )
215 fastlookup[ J ] = { SIG : ogre_vert_index }
221 '<position x="%s" y="%s" z="%s" />' %pos,
222 '<normal x="%s" y="%s" z="%s" />' %nor,
225 VB.append(
'\n'.join(xml) )
229 triangles.append( triangle )
230 VB.append(
'</vertexbuffer>' )
231 VB.append(
'</sharedgeometry>' )
233 file.write(
'\n'.join(VB) )
236 SMS = [
'<submeshes>']
239 '<submesh usesharedvertices="true" use32bitindexes="true" material="%s" operationtype="triangle_list">' %
'somemat',
240 '<faces count="%s">' %
'100',
242 for tri
in triangles:
245 assert isinstance(tri,tuple)
246 s =
'<face v1="%s" v2="%s" v3="%s" />' %tri
248 SM.append(
'</faces>' )
249 SM.append(
'</submesh>' )
251 file.write(
'\n'.join(SM) )
255 sys.exit(
'OK: module compiled and cached')
259 import hashlib, getpass, tempfile, configparser, subprocess, pickle
260 from xml.sax.saxutils
import XMLGenerator, quoteattr
275 self.
faces = (ctypes.c_uint * (Nfaces * 4))()
276 data.tessfaces.foreach_get(
'vertices_raw', self.
faces )
282 data.tessfaces.foreach_get(
'use_smooth', self.
faces_smooth )
288 if len( data.vertex_colors ):
289 vc = data.vertex_colors[0]
293 vc.data.foreach_get(
'color1', self.
vcolors1 )
294 self.vertex_colors.append( self.
vcolors1 )
297 vc.data.foreach_get(
'color2', self.
vcolors2 )
298 self.vertex_colors.append( self.
vcolors2 )
301 vc.data.foreach_get(
'color3', self.
vcolors3 )
302 self.vertex_colors.append( self.
vcolors3 )
305 vc.data.foreach_get(
'color4', self.
vcolors4 )
306 self.vertex_colors.append( self.
vcolors4 )
309 if data.uv_textures.active:
310 for layer
in data.uv_textures:
311 n = len(layer.data) * 8
312 a = (ctypes.c_float * n)()
313 layer.data.foreach_get(
'uv_raw', a )
314 self.uv_textures.append( a )
316 def save( blenderobject, path ):
317 cmesh = Mesh( blenderobject.data )
321 ctypes.addressof( cmesh.faces ),
322 ctypes.addressof( cmesh.faces_smooth ),
323 ctypes.addressof( cmesh.faces_material_index ),
324 ctypes.addressof( cmesh.vertex_positions ),
325 ctypes.addressof( cmesh.vertex_normals ),
329 print(
'Mesh dumped in %s seconds' % (time.time()-start))
332 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
333 if SCRIPT_DIR
not in sys.path:
334 sys.path.append( SCRIPT_DIR )
338 bpy.types.Object.use_avatar = BoolProperty(
339 name=
'enable avatar',
340 description=
'enables EC_Avatar',
342 bpy.types.Object.avatar_reference = StringProperty(
343 name=
'avatar reference',
344 description=
'sets avatar reference URL',
347 BoolProperty( name=
'enable avatar', description=
'enables EC_Avatar', default=
False)
351 bpy.types.Object.uid = IntProperty(
353 description=
"unique ID for Tundra",
354 default=0, min=0, max=2**14)
358 bpy.types.Object.use_draw_distance = BoolProperty(
359 name=
'enable draw distance',
360 description=
'use LOD draw distance',
362 bpy.types.Object.draw_distance = FloatProperty(
363 name=
'draw distance',
364 description=
'distance at which to begin drawing object',
365 default=0.0, min=0.0, max=10000.0)
366 bpy.types.Object.cast_shadows = BoolProperty(
368 description=
'cast shadows',
370 bpy.types.Object.use_multires_lod = BoolProperty(
371 name=
'Enable Multires LOD',
372 description=
'enables multires LOD',
374 bpy.types.Object.multires_lod_range = FloatProperty(
375 name=
'multires LOD range',
376 description=
'far distance at which multires is set to base level',
377 default=30.0, min=0.0, max=10000.0)
382 (
'NONE',
'NONE',
'no physics'),
383 (
'RIGID_BODY',
'RIGID_BODY',
'rigid body'),
384 (
'SOFT_BODY',
'SOFT_BODY',
'soft body'),
387 (
'NONE',
'NONE',
'no collision'),
388 (
'PRIMITIVE',
'PRIMITIVE',
'primitive collision type'),
389 (
'MESH',
'MESH',
'triangle-mesh or convex-hull collision type'),
390 (
'DECIMATED',
'DECIMATED',
'auto-decimated collision type'),
391 (
'COMPOUND',
'COMPOUND',
'children primitive compound collision type'),
392 (
'TERRAIN',
'TERRAIN',
'terrain (height map) collision type'),
395 bpy.types.Object.physics_mode = EnumProperty(
396 items = _physics_modes,
397 name =
'physics mode',
398 description=
'physics mode',
400 bpy.types.Object.physics_friction = FloatProperty(
401 name=
'Simple Friction',
402 description=
'physics friction',
403 default=0.1, min=0.0, max=1.0)
404 bpy.types.Object.physics_bounce = FloatProperty(
405 name=
'Simple Bounce',
406 description=
'physics bounce',
407 default=0.01, min=0.0, max=1.0)
408 bpy.types.Object.collision_terrain_x_steps = IntProperty(
409 name=
"Ogre Terrain: x samples",
410 description=
"resolution in X of height map",
411 default=64, min=4, max=8192)
412 bpy.types.Object.collision_terrain_y_steps = IntProperty(
413 name=
"Ogre Terrain: y samples",
414 description=
"resolution in Y of height map",
415 default=64, min=4, max=8192)
416 bpy.types.Object.collision_mode = EnumProperty(
417 items = _collision_modes,
418 name =
'primary collision mode',
419 description=
'collision mode',
421 bpy.types.Object.subcollision = BoolProperty(
422 name=
"collision compound",
423 description=
"member of a collision compound",
428 bpy.types.Speaker.play_on_load = BoolProperty(
431 bpy.types.Speaker.loop = BoolProperty(
434 bpy.types.Speaker.use_spatial = BoolProperty(
435 name=
'3D spatial sound',
441 (
'NONE',
'NONE',
'do not convert image'),
442 (
'bmp',
'bmp',
'bitmap format'),
443 (
'jpg',
'jpg',
'jpeg format'),
444 (
'gif',
'gif',
'gif format'),
445 (
'png',
'png',
'png format'),
446 (
'tga',
'tga',
'targa format'),
447 (
'dds',
'dds',
'nvidia dds format'),
450 bpy.types.Image.use_convert_format = BoolProperty(
451 name=
'use convert format',
454 bpy.types.Image.convert_format = EnumProperty(
455 name=
'convert to format',
456 description=
'converts to image format using imagemagick',
457 items=_IMAGE_FORMATS,
459 bpy.types.Image.jpeg_quality = IntProperty(
461 description=
"quality of jpeg",
462 default=80, min=0, max=100)
463 bpy.types.Image.use_color_quantize = BoolProperty(
464 name=
'use color quantize',
466 bpy.types.Image.use_color_quantize_dither = BoolProperty(
467 name=
'use color quantize dither',
469 bpy.types.Image.color_quantize = IntProperty(
470 name=
"color quantize",
471 description=
"reduce to N colors (requires ImageMagick)",
472 default=32, min=2, max=256)
473 bpy.types.Image.use_resize_half = BoolProperty(
474 name=
'resize by 1/2',
476 bpy.types.Image.use_resize_absolute = BoolProperty(
477 name=
'force image resize',
479 bpy.types.Image.resize_x = IntProperty(
481 description=
'only if image is larger than defined, use ImageMagick to resize it down',
482 default=256, min=2, max=4096)
483 bpy.types.Image.resize_y = IntProperty(
485 description=
'only if image is larger than defined, use ImageMagick to resize it down',
486 default=256, min=2, max=4096)
490 bpy.types.Material.ogre_depth_write = BoolProperty(
494 bpy.types.Material.ogre_depth_check = BoolProperty(
501 bpy.types.Material.ogre_alpha_to_coverage = BoolProperty(
507 name=
'multisample alpha edges',
509 bpy.types.Material.ogre_light_scissor = BoolProperty(
519 name=
'light scissor',
521 bpy.types.Material.ogre_light_clip_planes = BoolProperty(
522 name=
'light clip planes',
524 bpy.types.Material.ogre_normalise_normals = BoolProperty(
525 name=
'normalise normals',
527 description=
"Scaling objects causes normals to also change magnitude, which can throw off your lighting calculations. By default, the SceneManager detects this and will automatically re-normalise normals for any scaled object, but this has a cost. If you'd prefer to control this manually, call SceneManager::setNormaliseNormalsOnScale(false) and then use this option on materials which are sensitive to normals being resized.")
528 bpy.types.Material.ogre_lighting = BoolProperty(
531 name=
'dynamic lighting',
533 bpy.types.Material.ogre_colour_write = BoolProperty(
541 bpy.types.Material.use_fixed_pipeline = BoolProperty(
544 name=
'fixed pipeline',
546 bpy.types.Material.use_material_passes = BoolProperty(
549 name=
'use ogre extra material passes (layers)',
551 bpy.types.Material.use_in_ogre_material_pass = BoolProperty(
554 bpy.types.Material.use_ogre_advanced_options = BoolProperty(
555 name=
'Show Advanced Options',
557 bpy.types.Material.use_ogre_parent_material = BoolProperty(
558 name=
'Use Script Inheritance',
560 bpy.types.Material.ogre_parent_material = EnumProperty(
561 name=
"Script Inheritence",
562 description=
'ogre parent material class',
564 bpy.types.Material.ogre_polygon_mode = EnumProperty(
565 name=
'faces draw type',
566 description=
"ogre face draw mode",
567 items=[ (
'solid',
'solid',
'SOLID'),
568 (
'wireframe',
'wireframe',
'WIREFRAME'),
569 (
'points',
'points',
'POINTS') ],
571 bpy.types.Material.ogre_shading = EnumProperty(
572 name=
'hardware shading',
573 description=
"Sets the kind of shading which should be used for representing dynamic lighting for this pass.",
574 items=[ (
'flat',
'flat',
'FLAT'),
575 (
'gouraud',
'gouraud',
'GOURAUD'),
576 (
'phong',
'phong',
'PHONG') ],
578 bpy.types.Material.ogre_cull_hardware = EnumProperty(
579 name=
'hardware culling',
580 description=
"If the option 'cull_hardware clockwise' is set, all triangles whose vertices are viewed in clockwise order from the camera will be culled by the hardware.",
581 items=[ (
'clockwise',
'clockwise',
'CLOCKWISE'),
582 (
'anticlockwise',
'anticlockwise',
'COUNTER CLOCKWISE'),
583 (
'none',
'none',
'NONE') ],
585 bpy.types.Material.ogre_transparent_sorting = EnumProperty(
586 name=
'transparent sorting',
587 description=
"By default all transparent materials are sorted such that renderables furthest away from the camera are rendered first. This is usually the desired behaviour but in certain cases this depth sorting may be unnecessary and undesirable. If for example it is necessary to ensure the rendering order does not change from one frame to the next. In this case you could set the value to 'off' to prevent sorting.",
588 items=[ (
'on',
'on',
'ON'),
589 (
'off',
'off',
'OFF'),
590 (
'force',
'force',
'FORCE ON') ],
592 bpy.types.Material.ogre_illumination_stage = EnumProperty(
593 name=
'illumination stage',
594 description=
'When using an additive lighting mode (SHADOWTYPE_STENCIL_ADDITIVE or SHADOWTYPE_TEXTURE_ADDITIVE), the scene is rendered in 3 discrete stages, ambient (or pre-lighting), per-light (once per light, with shadowing) and decal (or post-lighting). Usually OGRE figures out how to categorise your passes automatically, but there are some effects you cannot achieve without manually controlling the illumination.',
595 items=[ (
'',
'',
'autodetect'),
596 (
'ambient',
'ambient',
'ambient'),
597 (
'per_light',
'per_light',
'lights'),
598 (
'decal',
'decal',
'decal') ],
603 (
'less_equal',
'less_equal',
'<='),
604 (
'less',
'less',
'<'),
605 (
'equal',
'equal',
'=='),
606 (
'not_equal',
'not_equal',
'!='),
607 (
'greater_equal',
'greater_equal',
'>='),
608 (
'greater',
'greater',
'>'),
609 (
'always_fail',
'always_fail',
'false'),
610 (
'always_pass',
'always_pass',
'true'),
613 bpy.types.Material.ogre_depth_func = EnumProperty(
614 items=_ogre_depth_func,
615 name=
'depth buffer function',
616 description=
'If depth checking is enabled (see depth_check) a comparison occurs between the depth value of the pixel to be written and the current contents of the buffer. This comparison is normally less_equal, i.e. the pixel is written if it is closer (or at the same distance) than the current contents',
617 default=
'less_equal')
619 _ogre_scene_blend_ops = [
620 (
'add',
'add',
'DEFAULT'),
621 (
'subtract',
'subtract',
'SUBTRACT'),
622 (
'reverse_subtract',
'reverse_subtract',
'REVERSE SUBTRACT'),
623 (
'min',
'min',
'MIN'),
624 (
'max',
'max',
'MAX'),
627 bpy.types.Material.ogre_scene_blend_op = EnumProperty(
628 items=_ogre_scene_blend_ops,
629 name=
'scene blending operation',
630 description=
'This directive changes the operation which is applied between the two components of the scene blending equation',
633 _ogre_scene_blend_types = [
634 (
'one zero',
'one zero',
'DEFAULT'),
635 (
'alpha_blend',
'alpha_blend',
"The alpha value of the rendering output is used as a mask. Equivalent to 'scene_blend src_alpha one_minus_src_alpha'"),
636 (
'add',
'add',
"The colour of the rendering output is added to the scene. Good for explosions, flares, lights, ghosts etc. Equivalent to 'scene_blend one one'."),
637 (
'modulate',
'modulate',
"The colour of the rendering output is multiplied with the scene contents. Generally colours and darkens the scene, good for smoked glass, semi-transparent objects etc. Equivalent to 'scene_blend dest_colour zero'"),
638 (
'colour_blend',
'colour_blend',
'Colour the scene based on the brightness of the input colours, but dont darken. Equivalent to "scene_blend src_colour one_minus_src_colour"'),
640 for mode
in 'dest_colour src_colour one_minus_dest_colour dest_alpha src_alpha one_minus_dest_alpha one_minus_src_alpha'.split():
641 _ogre_scene_blend_types.append( (
'one %s'%mode,
'one %s'%mode,
'') )
644 bpy.types.Material.ogre_scene_blend = EnumProperty(
645 items=_ogre_scene_blend_types,
647 description=
'blending operation of material to scene',
654 Q: I have hundres of objects, is there a way i can merge them on export only? 655 A: Yes, just add them to a group named starting with "merge", or link the group. 657 Q: Can i use subsurf or multi-res on a mesh with an armature? 660 Q: Can i use subsurf or multi-res on a mesh with shape animation? 663 Q: I don't see any objects when i export? 664 A: You must select the objects you wish to export. 666 Q: I don't see my animations when exported? 667 A: Make sure you created an NLA strip on the armature. 669 Q: Do i need to bake my IK and other constraints into FK on my armature before export? 675 ''' todo: Update the nonsense C:\Tundra2 paths from defaul config and fix this doc. 676 Additionally point to some doc how to build opengl only version on windows if that really is needed and 677 remove the old Tundra 7z link. ''' 679 _doc_installing_ =
''' 681 Installing the Addon: 682 You can simply copy io_export_ogreDotScene.py to your blender installation under blender/2.6x/scripts/addons/ 683 and enable it in the user-prefs interface (CTRL+ALT+U) 684 Or you can use blenders interface, under user-prefs, click addons, and click 'install-addon' 685 (its a good idea to delete the old version first) 690 2. Install Ogre Command Line tools to the default path: C:\\OgreCommandLineTools from http://www.ogre3d.org/download/tools 691 * These tools are used to create the binary Mesh from the .xml mesh generated by this plugin. 692 * Linux users may use above and Wine, or install from source, or install via apt-get install ogre-tools. 695 3. Install NVIDIA DDS Legacy Utilities - Install them to default path. 696 * http://developer.nvidia.com/object/dds_utilities_legacy.html 697 * Linux users will need to use Wine. 699 4. Install Image Magick 700 * http://www.imagemagick.org 702 5. Copy OgreMeshy to C:\\OgreMeshy 703 * If your using 64bit Windows, you may need to download a 64bit OgreMeshy 704 * Linux copy to your home folder. 707 * For latest Tundra releases see http://code.google.com/p/realxtend-naali/downloads/list 708 - You may need to tweak the config to tell your Tundra path or install to C:\Tundra2 709 * Old OpenGL only build can be found from http://blender2ogre.googlecode.com/files/realxtend-Tundra-2.1.2-OpenGL.7z 710 - Windows: extract to C:\Tundra2 711 - Linux: extract to ~/Tundra2 717 (
'xyz',
'xyz',
'no swapping'),
718 (
'xz-y',
'xz-y',
'ogre standard'),
719 (
'-xzy',
'-xzy',
'non standard'),
720 (
'aldeb',
'aldeb',
'invert x and z axis'),
724 if CONFIG[
'SWAP_AXIS'] ==
'xyz':
return vec
725 elif CONFIG[
'SWAP_AXIS'] ==
'xzy':
726 if len(vec) == 3:
return mathutils.Vector( [vec.x, vec.z, vec.y] )
727 elif len(vec) == 4:
return mathutils.Quaternion( [ vec.w, vec.x, vec.z, vec.y] )
728 elif CONFIG[
'SWAP_AXIS'] ==
'-xzy':
729 if len(vec) == 3:
return mathutils.Vector( [-vec.x, vec.z, vec.y] )
730 elif len(vec) == 4:
return mathutils.Quaternion( [ vec.w, -vec.x, vec.z, vec.y] )
731 elif CONFIG[
'SWAP_AXIS'] ==
'xz-y':
732 if len(vec) == 3:
return mathutils.Vector( [vec.x, vec.z, -vec.y] )
733 elif len(vec) == 4:
return mathutils.Quaternion( [ vec.w, vec.x, vec.z, -vec.y] )
734 elif CONFIG[
'SWAP_AXIS'] ==
'aldeb':
735 if len(vec) == 3:
return mathutils.Vector( [vec.x, -vec.z, vec.y] )
736 elif len(vec) == 4:
return mathutils.Quaternion( [ vec.w, vec.x, -vec.z, vec.y] )
738 print(
'unknown swap axis mode', CONFIG[
'SWAP_AXIS'] )
743 CONFIG_PATH = bpy.utils.user_resource(
'CONFIG', path=
'scripts', create=
True)
744 CONFIG_FILENAME =
'blender2ogre.pickle' 745 CONFIG_FILEPATH = os.path.join(CONFIG_PATH, CONFIG_FILENAME)
747 _CONFIG_DEFAULTS_ALL = {
748 'TUNDRA_STREAMING' :
True,
749 'COPY_SHADER_PROGRAMS' :
True,
750 'MAX_TEXTURE_SIZE' : 4096,
751 'SWAP_AXIS' :
'xz-y',
752 'ONLY_DEFORMABLE_BONES' :
False,
753 'ONLY_KEYFRAMED_BONES' :
False,
754 'OGRE_INHERIT_SCALE' :
False,
755 'FORCE_IMAGE_FORMAT' :
'NONE',
756 'TOUCH_TEXTURES' :
True,
760 'EXPORT_HIDDEN' :
True,
761 'FORCE_CAMERA' :
True,
762 'FORCE_LAMPS' :
True,
764 'MESH_OVERWRITE' :
True,
770 'TRIM_BONE_WEIGHTS' : 0.01,
774 'nuextremityPoints' : 0,
775 'generateEdgeLists' :
False,
776 'generateTangents' :
True,
777 'tangentSemantic' :
'tangent',
778 'tangentUseParity' : 4,
779 'tangentSplitMirrored' :
False,
780 'tangentSplitRotated' :
False,
781 'reorganiseBuffers' :
True,
782 'optimiseAnimations' :
True,
785 _CONFIG_TAGS_ =
'OGRETOOLS_XML_CONVERTER OGRETOOLS_MESH_MAGICK TUNDRA_ROOT OGRE_MESHY IMAGE_MAGICK_CONVERT NVCOMPRESS NVIDIATOOLS_EXE USER_MATERIALS SHADER_PROGRAMS TUNDRA_STREAMING'.split()
787 ''' todo: Change pretty much all of these windows ones. Make a smarter way of detecting 788 Ogre tools and Tundra from various default folders. Also consider making a installer that 789 ships Ogre cmd line tools to ease the setup steps for end users. ''' 791 _CONFIG_DEFAULTS_WINDOWS = {
792 'OGRETOOLS_XML_CONVERTER' :
'C:\\OgreCommandLineTools\\OgreXmlConverter.exe',
793 'OGRETOOLS_MESH_MAGICK' :
'C:\\OgreCommandLineTools\\MeshMagick.exe',
794 'TUNDRA_ROOT' :
'C:\\Tundra2',
795 'OGRE_MESHY' :
'C:\\OgreMeshy\\Ogre Meshy.exe',
796 'IMAGE_MAGICK_CONVERT' :
'C:\\Program Files\\ImageMagick\\convert.exe',
797 'NVIDIATOOLS_EXE' :
'C:\\Program Files\\NVIDIA Corporation\\DDS Utilities\\nvdxt.exe',
798 'USER_MATERIALS' :
'C:\\Tundra2\\media\\materials',
799 'SHADER_PROGRAMS' :
'C:\\Tundra2\\media\\materials\\programs',
800 'NVCOMPRESS' :
'C:\\nvcompress.exe' 803 _CONFIG_DEFAULTS_UNIX = {
804 'OGRETOOLS_XML_CONVERTER' :
'/usr/local/bin/OgreXMLConverter',
805 'OGRETOOLS_MESH_MAGICK' :
'/usr/local/bin/MeshMagick',
806 'TUNDRA_ROOT' :
'~/Tundra2',
807 'OGRE_MESHY' :
'~/OgreMeshy/Ogre Meshy.exe',
808 'IMAGE_MAGICK_CONVERT' :
'/usr/bin/convert',
809 'NVIDIATOOLS_EXE' :
'~/.wine/drive_c/Program Files/NVIDIA Corporation/DDS Utilities',
810 'USER_MATERIALS' :
'~/Tundra2/media/materials',
811 'SHADER_PROGRAMS' :
'~/Tundra2/media/materials/programs',
814 'NVCOMPRESS' :
'/usr/local/bin/nvcompress' 818 if sys.platform.startswith(
'linux')
or sys.platform.startswith(
'darwin')
or sys.platform.startswith(
'freebsd'):
819 for tag
in _CONFIG_DEFAULTS_UNIX:
820 path = _CONFIG_DEFAULTS_UNIX[ tag ]
821 if path.startswith(
'~'):
822 _CONFIG_DEFAULTS_UNIX[ tag ] = os.path.expanduser( path )
823 elif tag.startswith(
'OGRETOOLS')
and not os.path.isfile( path ):
824 _CONFIG_DEFAULTS_UNIX[ tag ] = os.path.join(
'/usr/bin', os.path.split( path )[-1] )
836 if os.path.isfile( CONFIG_FILEPATH ):
838 with open( CONFIG_FILEPATH,
'rb' )
as f:
839 CONFIG = pickle.load( f )
841 print(
'[ERROR]: Can not read config from %s' %CONFIG_FILEPATH)
843 for tag
in _CONFIG_DEFAULTS_ALL:
844 if tag
not in CONFIG:
845 CONFIG[ tag ] = _CONFIG_DEFAULTS_ALL[ tag ]
847 for tag
in _CONFIG_TAGS_:
848 if tag
not in CONFIG:
849 if sys.platform.startswith(
'win'):
850 CONFIG[ tag ] = _CONFIG_DEFAULTS_WINDOWS[ tag ]
851 elif sys.platform.startswith(
'linux')
or sys.platform.startswith(
'darwin')
or sys.platform.startswith(
'freebsd'):
852 CONFIG[ tag ] = _CONFIG_DEFAULTS_UNIX[ tag ]
854 print(
'ERROR: unknown platform' )
858 if sys.platform.startswith(
'win'):
861 registry_key = winreg.OpenKey(winreg.HKEY_CLASSES_ROOT,
r'Software\blender2ogre', 0, winreg.KEY_READ)
862 exe_install_dir = winreg.QueryValueEx(registry_key,
"Path")[0]
863 if exe_install_dir !=
"":
865 if os.path.isfile(exe_install_dir +
"OgreXmlConverter.exe"):
866 print (
"Using OgreXmlConverter from install path:", exe_install_dir +
"OgreXmlConverter.exe")
867 CONFIG[
'OGRETOOLS_XML_CONVERTER'] = exe_install_dir +
"OgreXmlConverter.exe" 872 if os.path.isfile(exe_install_dir +
"check-for-updates.exe"):
873 subprocess.Popen([exe_install_dir +
"check-for-updates.exe",
"/silent"])
874 except Exception
as e:
875 print(
"Exception while reading windows registry:", e)
878 for tag
in _CONFIG_TAGS_:
879 default = CONFIG[ tag ]
880 func = eval(
'lambda self,con: CONFIG.update( {"%s" : self.%s} )' %(tag,tag) )
881 if type(default)
is bool:
883 name=tag, description=
'updates bool setting', default=default,
884 options={
'SKIP_SAVE'}, update=func
887 prop = StringProperty(
888 name=tag, description=
'updates path setting', maxlen=128, default=default,
889 options={
'SKIP_SAVE'}, update=func
891 setattr( bpy.types.WindowManager, tag, prop )
895 CONFIG = load_config()
899 if os.path.isdir( CONFIG_PATH ):
901 with open( CONFIG_FILEPATH,
'wb' )
as f:
902 pickle.dump( CONFIG, f, -1 )
904 print(
'[ERROR]: Can not write to %s' %CONFIG_FILEPATH)
906 print(
'[ERROR:] Config directory does not exist %s' %CONFIG_PATH)
908 class Blender2Ogre_ConfigOp(bpy.types.Operator):
909 '''operator: saves current b2ogre configuration''' 910 bl_idname =
"ogre.save_config" 911 bl_label =
"save config file" 912 bl_options = {
'REGISTER'}
915 def poll(cls, context):
917 def invoke(self, context, event):
920 Report.messages.append(
'SAVED %s' %CONFIG_FILEPATH)
929 MISSING_MATERIAL =
''' 930 material _missing_material_ 937 ambient 0.1 0.1 0.1 1.0 938 diffuse 0.8 0.0 0.0 1.0 939 specular 0.5 0.5 0.5 1.0 12.5 940 emissive 0.3 0.3 0.3 1.0 948 def timer_diff_str(start):
949 return "%0.2f" % (time.time()-start)
951 def find_bone_index( ob, arm, groupidx):
952 if groupidx < len(ob.vertex_groups):
953 vg = ob.vertex_groups[ groupidx ]
955 for i,bone
in enumerate(arm.pose.bones):
956 if not bone.bone.use_deform
and CONFIG[
'ONLY_DEFORMABLE_BONES']:
958 if bone.name == vg.name:
961 print(
'WARNING: object vertex groups not in sync with armature', ob, arm, groupidx)
963 def mesh_is_smooth( mesh ):
964 for face
in mesh.tessfaces:
965 if face.use_smooth:
return True 967 def find_uv_layer_index( uvname, material=None ):
970 for mesh
in bpy.data.meshes:
971 if material
is None or material.name
in mesh.materials:
973 names = [ uv.name
for uv
in mesh.uv_textures ]
975 idx = names.index( uvname )
979 def has_custom_property( a, name ):
980 for prop
in a.items():
985 def is_strictly_simple_terrain( ob ):
987 if len(ob.data.vertices) != 4
and len(ob.data.tessfaces) != 1:
989 elif len(ob.modifiers) < 2:
991 elif ob.modifiers[0].type !=
'SUBSURF' or ob.modifiers[1].type !=
'DISPLACE':
993 elif ob.modifiers[0].subdivision_type !=
'SIMPLE':
995 elif ob.modifiers[1].direction !=
'Z':
1000 def get_image_textures( mat ):
1002 for s
in mat.texture_slots:
1003 if s
and s.texture.type ==
'IMAGE':
1007 def indent( level, *args ):
1018 def gather_instances():
1020 for ob
in bpy.context.scene.objects:
1021 if ob.data
and ob.data.users > 1:
1022 if ob.data
not in instances:
1023 instances[ ob.data ] = []
1024 instances[ ob.data ].append( ob )
1027 def select_instances( context, name ):
1028 for ob
in bpy.context.scene.objects:
1030 ob = bpy.context.scene.objects[ name ]
1032 inst = gather_instances()
1033 for ob
in inst[ ob.data ]: ob.select =
True 1034 bpy.context.scene.objects.active = ob
1036 def select_group( context, name, options={} ):
1037 for ob
in bpy.context.scene.objects:
1039 for grp
in bpy.data.groups:
1040 if grp.name == name:
1046 bpy.context.scene.objects.active = grp.objects[0]
1047 for ob
in grp.objects:
1052 def get_objects_using_materials( mats ):
1054 for ob
in bpy.data.objects:
1055 if ob.type ==
'MESH':
1056 for mat
in ob.data.materials:
1063 def get_materials_using_image( img ):
1065 for mat
in bpy.data.materials:
1066 for slot
in get_image_textures( mat ):
1067 if slot.texture.image == img:
1072 def get_parent_matrix( ob, objects ):
1074 return mathutils.Matrix(((1,0,0,0),(0,1,0,0),(0,0,1,0),(0,0,0,1)))
1076 if ob.parent
in objects:
1077 return ob.parent.matrix_world.copy()
1079 return get_parent_matrix(ob.parent, objects)
1081 def merge_group( group ):
1082 print(
'--------------- merge group ->', group )
1084 for ob
in group.objects:
1085 if ob.type ==
'MESH':
1086 print(
'\t group member', ob.name )
1087 o2 = ob.copy(); copies.append( o2 )
1088 o2.data = o2.to_mesh(bpy.context.scene,
True,
"PREVIEW")
1090 o2.modifiers.remove( o2.modifiers[0] )
1091 bpy.context.scene.objects.link( o2 )
1092 merged = merge( copies )
1093 merged.name = group.name
1094 merged.data.name = group.name
1097 def merge_objects( objects, name='_temp_', transform=None ):
1102 if ob.type ==
'MESH':
1103 o2 = ob.copy(); copies.append( o2 )
1104 o2.data = o2.to_mesh(bpy.context.scene,
True,
"PREVIEW")
1106 o2.modifiers.remove( o2.modifiers[0] )
1108 o2.matrix_world = transform * o2.matrix_local
1109 bpy.context.scene.objects.link( o2 )
1110 merged = merge( copies )
1112 merged.data.name = name
1115 def merge( objects ):
1116 print(
'MERGE', objects)
1117 for ob
in bpy.context.selected_objects:
1122 assert not ob.library
1123 bpy.context.scene.objects.active = ob
1124 bpy.ops.object.join()
1125 return bpy.context.active_object
1127 def get_merge_group( ob, prefix='merge' ):
1129 for grp
in ob.users_group:
1130 if grp.name.lower().startswith(prefix): m.append( grp )
1137 print(
'WARNING: an object can not be in two merge groups at the same time', ob)
1140 def wordwrap( txt ):
1142 for word
in txt.split(
' '):
1143 word = word.replace(
'\t',
' '*3)
1151 class RElement(object):
1152 def appendChild( self, child ):
1153 self.childNodes.append( child )
1155 def setAttribute( self, name, value ):
1156 self.attributes[name]=value
1158 def __init__(self, tag):
1160 self.childNodes = []
1161 self.attributes = {}
1163 def toprettyxml(self, lines, indent ):
1164 s =
'<%s ' % self.tagName
1165 sortedNames = sorted( self.attributes.keys() )
1166 for name
in sortedNames:
1167 value = self.attributes[name]
1168 if not isinstance(value, str):
1170 s +=
'%s=%s ' % (name, quoteattr(value))
1171 if not self.childNodes:
1172 s +=
'/>'; lines.append( (
' '*indent)+s )
1174 s +=
'>'; lines.append( (
' '*indent)+s )
1176 for child
in self.childNodes:
1177 child.toprettyxml( lines, indent )
1179 lines.append((
' '*indent) +
'</%s>' % self.tagName )
1181 class RDocument(object):
1183 self.documentElement =
None 1185 def appendChild(self, root):
1186 self.documentElement = root
1188 def createElement(self, tag):
1193 def toprettyxml(self):
1196 self.documentElement.toprettyxml(lines, indent)
1197 return '\n'.join(lines)
1199 class SimpleSaxWriter():
1200 def __init__(self, output, root_tag, root_attrs):
1201 self.output = output
1202 self.root_tag = root_tag
1204 output.write(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
1205 self.start_tag(root_tag, root_attrs)
1207 def _out_tag(self, name, attrs, isLeaf):
1209 self.output.write(
" " * self.indent)
1210 self.output.write(
"<%s" % name)
1211 sortedNames = sorted( attrs.keys() )
1212 for name
in sortedNames:
1213 value = attrs[ name ]
1215 if not isinstance(value, str):
1218 self.output.write(
" %s=%s" % (name, quoteattr(value)))
1220 self.output.write(
"/")
1223 self.output.write(
">\n")
1225 def start_tag(self, name, attrs):
1226 self._out_tag(name, attrs,
False)
1228 def end_tag(self, name):
1230 self.output.write(
" " * self.indent)
1231 self.output.write(
"</%s>\n" % name)
1233 def leaf_tag(self, name, attrs):
1234 self._out_tag(name, attrs,
True)
1237 self.end_tag( self.root_tag )
1241 class ReportSingleton(object):
1246 bpy.ops.wm.call_menu( name=
'MiniReport' )
1254 self.armature_animations = []
1255 self.shape_animations = []
1258 self.orig_vertices = 0
1268 ex = [
'Extended Report:']
1270 r.append(
' ERRORS:' )
1271 for a
in self.errors: r.append(
' - %s' %a )
1276 r.append(
' WARNINGS:' )
1277 for a
in self.warnings: r.append(
' - %s' %a )
1280 r.append(
' MESSAGES:' )
1281 for a
in self.messages: r.append(
' - %s' %a )
1283 r.append(
' PATHS:' )
1284 for a
in self.paths: r.append(
' - %s' %a )
1287 r.append(
' Original Vertices: %s' %self.orig_vertices)
1288 r.append(
' Exported Vertices: %s' %self.vertices )
1289 r.append(
' Original Faces: %s' %self.faces )
1290 r.append(
' Exported Triangles: %s' %self.triangles )
1293 for tag
in 'meshes lights cameras armatures armature_animations shape_animations materials textures'.split():
1294 attr = getattr(self, tag)
1296 name = tag.replace(
'_',
' ').upper()
1297 r.append(
' %s: %s' %(name, len(attr)) )
1299 ex.append(
' %s:' %name )
1300 for a
in attr: ex.append(
' . %s' %a )
1302 txt =
'\n'.join( r )
1303 ex =
'\n'.join( ex )
1310 Report = ReportSingleton()
1312 class MiniReport(bpy.types.Menu):
1313 bl_label =
"Mini-Report | (see console for full report)" 1314 def draw(self, context):
1315 layout = self.layout
1316 txt = Report.report()
1317 for line
in txt.splitlines():
1318 layout.label(text=line)
1322 def _get_proxy_decimate_mod( ob ):
1324 for child
in ob.children:
1325 if child.subcollision
and child.name.startswith(
'DECIMATED'):
1326 for mod
in child.modifiers:
1327 if mod.type ==
'DECIMATE':
1330 def bake_terrain( ob, normalize=True ):
1331 assert ob.collision_mode ==
'TERRAIN' 1333 for child
in ob.children:
1334 if child.subcollision
and child.name.startswith(
'TERRAIN'):
1338 data = terrain.to_mesh(bpy.context.scene,
True,
"PREVIEW")
1339 raw = [ v.co.z
for v
in data.vertices ]
1347 for x
in range( ob.collision_terrain_x_steps ):
1349 for y
in range( ob.collision_terrain_y_steps ):
1350 v = data.vertices[ i ]
1352 z = (v.co.z - Zmin) * m
1360 return {
'data':rows,
'min':Zmin,
'max':Zmax,
'depth':depth}
1362 def save_terrain_as_NTF( path, ob ):
1363 info = bake_terrain( ob )
1364 url = os.path.join( path,
'%s.ntf' % clean_object_name(ob.data.name) )
1367 buf = array.array(
"I")
1368 xs = ob.collision_terrain_x_steps
1369 ys = ob.collision_terrain_y_steps
1370 xpatches = int(xs/16)
1371 ypatches = int(ys/16)
1372 header = [ xpatches, ypatches ]
1373 buf.fromlist( header )
1377 for x
in range( xpatches ):
1378 for y
in range( ypatches ):
1382 v = rows[ (x*16)+i ][ (y*16)+j ]
1384 buf = array.array(
"f")
1385 buf.fromlist( patch )
1388 path,name = os.path.split(url)
1390 'url':url,
'min':info[
'min'],
'max':info[
'max'],
'path':path,
'name':name,
1391 'xpatches': xpatches,
'ypatches': ypatches,
1392 'depth':info[
'depth'],
1396 class OgreCollisionOp(bpy.types.Operator):
1397 '''Ogre Collision''' 1398 bl_idname =
"ogre.set_collision" 1399 bl_label =
"modify collision" 1400 bl_options = {
'REGISTER'}
1401 MODE = StringProperty(name=
"toggle mode", maxlen=32, default=
"disable")
1404 def poll(cls, context):
1405 if context.active_object
and context.active_object.type ==
'MESH':
1408 def get_subcollisions( self, ob, create=True ):
1409 r = get_subcollisions( ob )
1410 if not r
and create:
1411 method = getattr(self,
'create_%s'%ob.collision_mode)
1413 p.name =
'%s.%s' %(ob.collision_mode, ob.name)
1414 p.subcollision =
True 1418 def create_DECIMATED(self, ob):
1420 bpy.context.scene.objects.link( child )
1421 child.matrix_local = mathutils.Matrix()
1423 child.hide_select =
True 1424 child.draw_type =
'WIRE' 1426 child.lock_location = [
True]*3
1427 child.lock_rotation = [
True]*3
1428 child.lock_scale = [
True]*3
1429 decmod = child.modifiers.new(
'proxy', type=
'DECIMATE')
1433 def create_TERRAIN(self, ob):
1434 x = ob.collision_terrain_x_steps
1435 y = ob.collision_terrain_y_steps
1438 bpy.ops.mesh.primitive_grid_add(
1442 grid = bpy.context.active_object
1443 assert grid.name.startswith(
'Grid')
1444 grid.collision_terrain_x_steps = x
1445 grid.collision_terrain_y_steps = y
1447 x,y,z = ob.dimensions
1454 grid.location.z -= z/2
1455 grid.data.show_all_edges =
True 1456 grid.draw_type =
'WIRE' 1457 grid.hide_select =
True 1459 grid.lock_location = [
True]*3
1460 grid.lock_rotation = [
True]*3
1461 grid.lock_scale = [
True]*3
1463 bpy.context.scene.objects.active = ob
1464 mod = grid.modifiers.new(name=
'temp', type=
'SHRINKWRAP')
1465 mod.wrap_method =
'PROJECT' 1466 mod.use_project_z =
True 1468 mod.cull_face =
'FRONT' 1471 def invoke(self, context, event):
1472 ob = context.active_object
1476 if ':' in self.MODE:
1477 mode, subtype = self.MODE.split(
':')
1479 if subtype
in 'BOX SPHERE CYLINDER CONE CAPSULE'.split():
1480 ob.draw_bounds_type = subtype
1482 ob.draw_bounds_type =
'POLYHEDRON' 1483 ob.game.collision_bounds_type = subtype
1486 ob.collision_mode = mode
1488 if ob.data.show_all_edges:
1489 ob.data.show_all_edges =
False 1490 if ob.show_texture_space:
1491 ob.show_texture_space =
False 1493 ob.show_bounds =
False 1495 ob.show_wire =
False 1496 for child
in ob.children:
1497 if child.subcollision
and not child.hide:
1501 game.use_ghost =
True 1502 game.use_collision_bounds =
False 1503 elif mode ==
'PRIMITIVE':
1504 game.use_ghost =
False 1505 game.use_collision_bounds =
True 1506 ob.show_bounds =
True 1507 elif mode ==
'MESH':
1508 game.use_ghost =
False 1509 game.use_collision_bounds =
True 1511 if game.collision_bounds_type ==
'CONVEX_HULL':
1512 ob.show_texture_space =
True 1514 ob.data.show_all_edges =
True 1515 elif mode ==
'DECIMATED':
1516 game.use_ghost =
True 1517 game.use_collision_bounds =
False 1518 game.use_collision_compound =
True 1519 proxy = self.get_subcollisions(ob)[0]
1520 if proxy.hide: proxy.hide =
False 1521 ob.game.use_collision_compound =
True 1522 mod = _get_proxy_decimate_mod( ob )
1523 mod.show_viewport =
True 1524 if not proxy.select:
1525 proxy.hide_select =
False 1527 proxy.hide_select =
True 1528 if game.collision_bounds_type ==
'CONVEX_HULL':
1529 ob.show_texture_space =
True 1530 elif mode ==
'TERRAIN':
1531 game.use_ghost =
True 1532 game.use_collision_bounds =
False 1533 game.use_collision_compound =
True 1534 proxy = self.get_subcollisions(ob)[0]
1537 elif mode ==
'COMPOUND':
1538 game.use_ghost =
True 1539 game.use_collision_bounds =
False 1540 game.use_collision_compound =
True 1549 class PANEL_Physics(bpy.types.Panel):
1550 bl_space_type =
'VIEW_3D' 1551 bl_region_type =
'UI' 1552 bl_label =
"Physics" 1555 def poll(cls, context):
1556 if context.active_object:
1561 def draw(self, context):
1562 layout = self.layout
1563 ob = context.active_object
1566 if ob.type !=
'MESH':
1568 elif ob.subcollision ==
True:
1571 box.label(text=
'object is a collision proxy for: %s' %ob.parent.name)
1573 box.label(text=
'WARNING: collision proxy missing parent')
1577 box.prop(ob,
'physics_mode')
1578 if ob.physics_mode !=
'NONE':
1579 box.prop(game,
'mass', text=
'Mass')
1580 box.prop(ob,
'physics_friction', text=
'Friction', slider=
True)
1581 box.prop(ob,
'physics_bounce', text=
'Bounce', slider=
True)
1583 box.label(text=
"Damping:")
1584 box.prop(game,
'damping', text=
'Translation', slider=
True)
1585 box.prop(game,
'rotation_damping', text=
'Rotation', slider=
True)
1587 box.label(text=
"Velocity:")
1588 box.prop(game,
"velocity_min", text=
"Minimum")
1589 box.prop(game,
"velocity_max", text=
"Maximum")
1592 class PANEL_Collision(bpy.types.Panel):
1593 bl_space_type =
'VIEW_3D' 1594 bl_region_type =
'UI' 1595 bl_label =
"Collision" 1598 def poll(cls, context):
1599 if context.active_object:
1604 def draw(self, context):
1605 layout = self.layout
1606 ob = context.active_object
1609 if ob.type !=
'MESH':
1611 elif ob.subcollision ==
True:
1614 box.label(text=
'object is a collision proxy for: %s' %ob.parent.name)
1616 box.label(text=
'WARNING: collision proxy missing parent')
1619 mode = ob.collision_mode
1622 op = box.operator(
'ogre.set_collision', text=
'Enable Collision', icon=
'PHYSICS' )
1623 op.MODE =
'PRIMITIVE:%s' %game.collision_bounds_type
1625 prim = game.collision_bounds_type
1628 op = box.operator(
'ogre.set_collision', text=
'Disable Collision', icon=
'X' )
1630 box.prop(game,
"collision_margin", text=
"Collision Margin", slider=
True)
1633 if mode ==
'PRIMITIVE':
1634 box.label(text=
'Primitive: %s' %prim)
1636 box.label(text=
'Primitive')
1640 'BOX':
'MESH_CUBE',
'SPHERE':
'MESH_UVSPHERE',
'CYLINDER':
'MESH_CYLINDER',
1641 'CONE':
'MESH_CONE',
'CAPSULE':
'META_CAPSULE'}
1642 for a
in 'BOX SPHERE CYLINDER CONE CAPSULE'.split():
1643 if prim == a
and mode ==
'PRIMITIVE':
1644 op = row.operator(
'ogre.set_collision', text=
'', icon=_icons[a], emboss=
True )
1645 op.MODE =
'PRIMITIVE:%s' %a
1647 op = row.operator(
'ogre.set_collision', text=
'', icon=_icons[a], emboss=
False )
1648 op.MODE =
'PRIMITIVE:%s' %a
1651 if mode ==
'MESH': box.label(text=
'Mesh: %s' %prim.split(
'_')[0] )
1652 else: box.label(text=
'Mesh')
1654 row.label(text=
'- - - - - - - - - - - - - -')
1655 _icons = {
'TRIANGLE_MESH':
'MESH_ICOSPHERE',
'CONVEX_HULL':
'SURFACE_NCURVE'}
1656 for a
in 'TRIANGLE_MESH CONVEX_HULL'.split():
1657 if prim == a
and mode ==
'MESH':
1658 op = row.operator(
'ogre.set_collision', text=
'', icon=_icons[a], emboss=
True )
1659 op.MODE =
'MESH:%s' %a
1661 op = row.operator(
'ogre.set_collision', text=
'', icon=_icons[a], emboss=
False )
1662 op.MODE =
'MESH:%s' %a
1665 if mode ==
'DECIMATED':
1666 box.label(text=
'Decimate: %s' %prim.split(
'_')[0] )
1668 mod = _get_proxy_decimate_mod( ob )
1670 row.label(text=
'Faces: %s' %mod.face_count )
1671 box.prop( mod,
'ratio', text=
'' )
1673 box.label(text=
'Decimate')
1675 row.label(text=
'- - - - - - - - - - - - - -')
1677 _icons = {
'TRIANGLE_MESH':
'MESH_ICOSPHERE',
'CONVEX_HULL':
'SURFACE_NCURVE'}
1678 for a
in 'TRIANGLE_MESH CONVEX_HULL'.split():
1679 if prim == a
and mode ==
'DECIMATED':
1680 op = row.operator(
'ogre.set_collision', text=
'', icon=_icons[a], emboss=
True )
1681 op.MODE =
'DECIMATED:%s' %a
1683 op = row.operator(
'ogre.set_collision', text=
'', icon=_icons[a], emboss=
False )
1684 op.MODE =
'DECIMATED:%s' %a
1687 if mode ==
'TERRAIN':
1688 terrain = get_subcollisions( ob )[0]
1689 if ob.collision_terrain_x_steps != terrain.collision_terrain_x_steps
or ob.collision_terrain_y_steps != terrain.collision_terrain_y_steps:
1690 op = box.operator(
'ogre.set_collision', text=
'Rebuild Terrain', icon=
'MESH_GRID' )
1693 box.label(text=
'Terrain:')
1695 row.prop( ob,
'collision_terrain_x_steps',
'X' )
1696 row.prop( ob,
'collision_terrain_y_steps',
'Y' )
1698 box.prop( terrain.modifiers[0],
'cull_face', text=
'Cull' )
1699 box.prop( terrain,
'location' )
1701 op = box.operator(
'ogre.set_collision', text=
'Terrain Collision', icon=
'MESH_GRID' )
1705 if mode ==
'COMPOUND':
1706 op = box.operator(
'ogre.set_collision', text=
'Compound Collision', icon=
'ROTATECOLLECTION' )
1708 op = box.operator(
'ogre.set_collision', text=
'Compound Collision', icon=
'ROTATECOLLECTION' )
1709 op.MODE =
'COMPOUND' 1712 class PANEL_Configure(bpy.types.Panel):
1713 bl_space_type =
'PROPERTIES' 1714 bl_region_type =
'WINDOW' 1715 bl_context =
"scene" 1716 bl_label =
"Ogre Configuration File" 1718 def draw(self, context):
1719 layout = self.layout
1720 op = layout.operator(
'ogre.save_config', text=
'update config file', icon=
'FILE' )
1721 for tag
in _CONFIG_TAGS_:
1722 layout.prop( context.window_manager, tag )
1729 class PopUpDialogOperator(bpy.types.Operator):
1730 bl_idname =
"object.popup_dialog_operator" 1731 bl_label =
"blender2ogre" 1734 print(
"dialog Start")
1739 def execute(self, context):
1741 return {
'RUNNING_MODAL'}
1743 def draw(self, context):
1748 global popup_message
1749 layout = self.layout
1750 col = layout.column()
1751 col.label(popup_message,
'ERROR')
1753 def invoke(self, context, event):
1754 wm = context.window_manager
1755 wm.invoke_popup(self)
1756 wm.modal_handler_add(self)
1757 return {
'RUNNING_MODAL'}
1759 def modal(self, context, event):
1761 if event.type ==
'LEFTMOUSE':
1762 print (
"Left mouse")
1765 elif event.type
in (
'RIGHTMOUSE',
'ESC'):
1766 print (
"right mouse")
1769 print(
"running modal")
1770 return {
'RUNNING_MODAL'}
1772 def show_dialog(message):
1773 global popup_message
1774 popup_message = message
1775 bpy.ops.object.popup_dialog_operator(
'INVOKE_DEFAULT')
1779 _game_logic_intro_doc_ =
''' 1782 Blender contains a fully functional game engine (BGE) that is highly useful for learning the concepts of game programming by breaking it down into three simple parts: Sensor, Controller, and Actuator. An Ogre based game engine will likely have similar concepts in its internal API and game logic scripting. Without a custom interface to define game logic, very often game designers may have to resort to having programmers implement their ideas in purely handwritten script. This is prone to breakage because object names then end up being hard-coded. Not only does this lead to non-reusable code, its also a slow process. Why should we have to resort to this when Blender already contains a very rich interface for game logic? By hijacking a subset of the BGE interface we can make this workflow between game designer and game programmer much better. 1784 The OgreDocScene format can easily be extened to include extra game logic data. While the BGE contains some features that can not be easily mapped to other game engines, there are many are highly useful generic features we can exploit, including many of the Sensors and Actuators. Blender uses the paradigm of: 1. Sensor -> 2. Controller -> 3. Actuator. In pseudo-code, this can be thought of as: 1. on-event -> 2. conditional logic -> 3. do-action. The designer is most often concerned with the on-events (the Sensors), and the do-actions (the Actuators); and the BGE interface provides a clear way for defining and editing those. Its a harder task to provide a good interface for the conditional logic (Controller), that is flexible enough to fit everyones different Ogre engine and requirements, so that is outside the scope of this exporter at this time. A programmer will still be required to fill the gap between Sensor and Actuator, but hopefully his work is greatly reduced and can write more generic/reuseable code. 1786 The rules for which Sensors trigger which Actuators is left undefined, as explained above we are hijacking the BGE interface not trying to export and reimplement everything. BGE Controllers and all links are ignored by the exporter, so whats the best way to define Sensor/Actuator relationships? One convention that seems logical is to group Sensors and Actuators by name. More complex syntax could be used in Sensor/Actuators names, or they could be completely ignored and instead all the mapping is done by the game programmer using other rules. This issue is not easily solved so designers and the engine programmers will have to decide upon their own conventions, there is no one size fits all solution. 1789 _ogre_logic_types_doc_ =
''' 1798 Supported Actuators: 1809 The most common thing a designer will want to do is have an event trigger an animation. The BGE contains an Actuator called "Shape Action", with useful properties like: start/end frame, and blending. It also contains a property called "Action" but this is hidden because the exporter ignores action names and instead uses the names of NLA strips when exporting Ogre animation tracks. The current workaround is to hijack the "Frame Property" attribute and change its name to "animation". The designer can then simply type the name of the animation track (NLA strip). Any custom syntax could actually be implemented here for calling animations, its up to the engine programmer to define how this field will be used. For example: "*.explode" could be implemented to mean "on all objects" play the "explode" animation. 1812 class _WrapLogic(object):
1813 SwapName = {
'frame_property' :
'animation' }
1815 def __init__(self, node):
1817 self.name = node.name
1818 self.type = node.type
1820 def widget(self, layout):
1823 row.label( text=self.type )
1825 row.prop( self.node,
'name', text=
'' )
1826 if self.type
in self.TYPES:
1827 for name
in self.TYPES[ self.type ]:
1828 if name
in self.SwapName:
1829 box.prop( self.node, name, text=self.SwapName[name] )
1831 box.prop( self.node, name )
1833 def xml( self, doc ):
1834 g = doc.createElement( self.LogicType )
1835 g.setAttribute(
'name', self.name)
1836 g.setAttribute(
'type', self.type)
1837 if self.type
in self.TYPES:
1838 for name
in self.TYPES[ self.type ]:
1839 attr = getattr( self.node, name )
1840 if name
in self.SwapName: name = self.SwapName[name]
1841 a = doc.createElement(
'component' )
1843 a.setAttribute(
'name', name)
1844 if attr
is None: a.setAttribute(
'type',
'POINTER' )
1845 else: a.setAttribute(
'type',
type(attr).__name__)
1847 if type(attr)
in (float, int, str, bool): a.setAttribute(
'value', str(attr))
1848 elif not attr: a.setAttribute(
'value',
'')
1849 elif hasattr(attr,
'filepath'): a.setAttribute(
'value', attr.filepath)
1850 elif hasattr(attr,
'name'): a.setAttribute(
'value', attr.name)
1851 elif hasattr(attr,
'x')
and hasattr(attr,
'y')
and hasattr(attr,
'z'):
1852 a.setAttribute(
'value',
'%s %s %s' %(attr.x, attr.y, attr.z))
1854 print(
'ERROR: unknown type', attr)
1857 class WrapSensor( _WrapLogic ):
1858 LogicType =
'sensor' 1860 'COLLISION': [
'property'],
1861 'MESSAGE' : [
'subject'],
1862 'NEAR' : [
'property',
'distance',
'reset_distance'],
1863 'RADAR' : [
'property',
'axis',
'angle',
'distance' ],
1864 'RAY' : [
'ray_type',
'property',
'material',
'axis',
'range',
'use_x_ray'],
1865 'TOUCH' : [
'material'],
1868 class WrapActuator( _WrapLogic ):
1869 LogicType =
'actuator' 1871 'CAMERA' : [
'object',
'height',
'min',
'max',
'axis'],
1872 'CONSTRAINT' : [
'mode',
'limit',
'limit_min',
'limit_max',
'damping'],
1873 'MESSAGE' : [
'to_property',
'subject',
'body_message'],
1874 'OBJECT' :
'damping derivate_coefficient force force_max_x force_max_y force_max_z force_min_x force_min_y force_min_z integral_coefficient linear_velocity mode offset_location offset_rotation proportional_coefficient reference_object torque use_local_location use_local_rotation use_local_torque use_servo_limit_x use_servo_limit_y use_servo_limit_z'.split(),
1875 'SOUND' :
'cone_inner_angle_3d cone_outer_angle_3d cone_outer_gain_3d distance_3d_max distance_3d_reference gain_3d_max gain_3d_min mode pitch rolloff_factor_3d sound use_sound_3d volume'.split(),
1876 'VISIBILITY' :
'apply_to_children use_occlusion use_visible'.split(),
1877 'SHAPE_ACTION' :
'frame_blend_in frame_end frame_property frame_start mode property use_continue_last_frame'.split(),
1878 'EDIT_OBJECT' :
'dynamic_operation linear_velocity mass mesh mode object time track_object use_3d_tracking use_local_angular_velocity use_local_linear_velocity use_replace_display_mesh use_replace_physics_mesh'.split(),
1881 class _OgreMatPass( object ):
1882 bl_space_type =
'PROPERTIES' 1883 bl_region_type =
'WINDOW' 1884 bl_context =
"material" 1887 def poll(cls, context):
1888 if context.active_object
and context.active_object.active_material
and context.active_object.active_material.use_material_passes:
1891 def draw(self, context):
1892 if not hasattr(context,
"material"):
1894 if not context.active_object:
1896 if not context.active_object.active_material:
1899 mat = context.material
1901 slot = context.material_slot
1902 layout = self.layout
1904 if mat.use_material_passes:
1906 nodes = bpyShaders.get_or_create_material_passes( mat )
1907 node = nodes[ self.INDEX ]
1909 if node.material: split.prop( node.material,
'use_in_ogre_material_pass', text=
'' )
1910 split.prop( node,
'material' )
1911 if not node.material:
1912 op = split.operator(
'ogre.helper_create_attach_material_layer', icon=
"PLUS", text=
'' )
1913 op.INDEX = self.INDEX
1914 if node.material
and node.material.use_in_ogre_material_pass:
1916 ogre_material_panel( dbb, node.material, parent=mat )
1917 ogre_material_panel_extra( dbb, node.material )
1919 class _create_new_material_layer_helper(bpy.types.Operator):
1920 '''helper to create new material layer''' 1921 bl_idname =
"ogre.helper_create_attach_material_layer" 1922 bl_label =
"creates and assigns new material to layer" 1923 bl_options = {
'REGISTER'}
1924 INDEX = IntProperty(name=
"material layer index", description=
"index", default=0, min=0, max=8)
1927 def poll(cls, context):
1928 if context.active_object
and context.active_object.active_material
and context.active_object.active_material.use_material_passes:
1931 def execute(self, context):
1932 mat = context.active_object.active_material
1933 nodes = bpyShaders.get_or_create_material_passes( mat )
1934 node = nodes[ self.INDEX ]
1935 node.material = bpy.data.materials.new( name=
'%s.LAYER%s'%(mat.name,self.INDEX) )
1936 node.material.use_fixed_pipeline =
False 1937 node.material.offset_z = (self.INDEX*2) + 2
1943 class PANEL_properties_window_ogre_material( bpy.types.Panel ):
1944 bl_space_type =
'PROPERTIES' 1945 bl_region_type =
'WINDOW' 1946 bl_context =
"material" 1947 bl_label =
"Ogre Material (base pass)" 1950 def poll( self, context ):
1951 if not hasattr(context,
"material"):
return False 1952 if not context.active_object:
return False 1953 if not context.active_object.active_material:
return False 1956 def draw(self, context):
1957 mat = context.material
1959 slot = context.material_slot
1960 layout = self.layout
1961 if not mat.use_material_passes:
1963 box.operator(
'ogre.force_setup_material_passes', text=
"Ogre Material Layers", icon=
'SCENE_DATA' )
1965 ogre_material_panel( layout, mat )
1966 ogre_material_panel_extra( layout, mat )
1969 class MatPass1( _OgreMatPass, bpy.types.Panel ): INDEX = 0; bl_label =
"Ogre Material (pass%s)"%str(INDEX+1)
1971 class MatPass2( _OgreMatPass, bpy.types.Panel ): INDEX = 1; bl_label =
"Ogre Material (pass%s)"%str(INDEX+1)
1973 class MatPass3( _OgreMatPass, bpy.types.Panel ): INDEX = 2; bl_label =
"Ogre Material (pass%s)"%str(INDEX+1)
1975 class MatPass4( _OgreMatPass, bpy.types.Panel ): INDEX = 3; bl_label =
"Ogre Material (pass%s)"%str(INDEX+1)
1977 class MatPass5( _OgreMatPass, bpy.types.Panel ): INDEX = 4; bl_label =
"Ogre Material (pass%s)"%str(INDEX+1)
1979 class MatPass6( _OgreMatPass, bpy.types.Panel ): INDEX = 5; bl_label =
"Ogre Material (pass%s)"%str(INDEX+1)
1981 class MatPass7( _OgreMatPass, bpy.types.Panel ): INDEX = 6; bl_label =
"Ogre Material (pass%s)"%str(INDEX+1)
1983 class MatPass8( _OgreMatPass, bpy.types.Panel ): INDEX = 7; bl_label =
"Ogre Material (pass%s)"%str(INDEX+1)
1986 class PANEL_Textures(bpy.types.Panel):
1987 bl_space_type =
'PROPERTIES' 1988 bl_region_type =
'WINDOW' 1989 bl_context =
"texture" 1990 bl_label =
"Ogre Texture" 1993 def poll(cls, context):
1994 if not hasattr(context,
"texture_slot"):
1998 def draw(self, context):
2001 layout = self.layout
2003 slot = context.texture_slot
2004 if not slot
or not slot.texture:
2007 btype = slot.blend_type
2008 ex =
False; texop =
None 2009 if btype
in TextureUnit.colour_op:
2010 if btype==
'MIX' and slot.use_map_alpha
and not slot.use_stencil:
2011 if slot.diffuse_color_factor >= 1.0:
2012 texop =
'alpha_blend' 2014 texop = TextureUnit.colour_op_ex[ btype ]
2016 elif btype==
'MIX' and slot.use_map_alpha
and slot.use_stencil:
2017 texop =
'blend_current_alpha'; ex=
True 2018 elif btype==
'MIX' and not slot.use_map_alpha
and slot.use_stencil:
2019 texop =
'blend_texture_alpha'; ex=
True 2021 texop = TextureUnit.colour_op[ btype ]
2022 elif btype
in TextureUnit.colour_op_ex:
2023 texop = TextureUnit.colour_op_ex[ btype ]
2030 row.prop(slot,
"blend_type", text=texop, icon=
'NEW')
2032 row.prop(slot,
"blend_type", text=texop)
2034 row.prop(slot,
"blend_type", text=
'(invalid option)')
2037 row.prop(slot,
"use_stencil", text=
"")
2038 row.prop(slot,
"use_map_alpha", text=
"")
2039 if texop ==
'blend_manual':
2041 row.label(text=
"Alpha:")
2042 row.prop(slot,
"diffuse_color_factor", text=
"")
2044 if hasattr(slot.texture,
'image')
and slot.texture.image:
2046 n =
'(invalid option)' 2047 if slot.texture.extension
in TextureUnit.tex_address_mode:
2048 n = TextureUnit.tex_address_mode[ slot.texture.extension ]
2049 row.prop(slot.texture,
"extension", text=n)
2050 if slot.texture.extension ==
'CLIP':
2051 row.prop(slot,
"color", text=
"Border Color")
2054 if slot.texture_coords ==
'UV':
2055 row.prop(slot,
"texture_coords", text=
"", icon=
'GROUP_UVS')
2056 row.prop(slot,
"uv_layer", text=
'Layer')
2057 elif slot.texture_coords ==
'REFLECTION':
2058 row.prop(slot,
"texture_coords", text=
"", icon=
'MOD_UVPROJECT')
2059 n =
'(invalid option)' 2060 if slot.mapping
in 'FLAT SPHERE'.split(): n =
'' 2061 row.prop(slot,
"mapping", text=n)
2063 row.prop(slot,
"texture_coords", text=
"(invalid mapping option)")
2066 split = layout.row()
2068 box.prop(slot,
"offset", text=
"XY=offset, Z=rotation")
2070 box.prop(slot,
"scale", text=
"XY=scale (Z ignored)")
2074 row.label(text=
'scrolling animation')
2077 row.prop(slot,
"use_map_scatter", text=
"")
2079 row.prop(slot,
"density_factor", text=
"X")
2080 row.prop(slot,
"emission_factor", text=
"Y")
2084 row.label(text=
'rotation animation')
2085 row.prop(slot,
"emission_color_factor", text=
"")
2086 row.prop(slot,
"use_from_dupli", text=
"")
2089 if hasattr(slot.texture,
'image')
and slot.texture.image:
2090 img = slot.texture.image
2093 row.prop( img,
'use_convert_format' )
2094 if img.use_convert_format:
2095 row.prop( img,
'convert_format' )
2096 if img.convert_format ==
'jpg':
2097 box.prop( img,
'jpeg_quality' )
2100 row.prop( img,
'use_color_quantize', text=
'Reduce Colors' )
2101 if img.use_color_quantize:
2102 row.prop( img,
'use_color_quantize_dither', text=
'dither' )
2103 row.prop( img,
'color_quantize', text=
'colors' )
2106 row.prop( img,
'use_resize_half' )
2107 if not img.use_resize_half:
2108 row.prop( img,
'use_resize_absolute' )
2109 if img.use_resize_absolute:
2111 row.prop( img,
'resize_x' )
2112 row.prop( img,
'resize_y' )
2116 class OgreMeshyPreviewOp(bpy.types.Operator):
2117 '''helper to open ogremeshy''' 2118 bl_idname =
'ogremeshy.preview' 2119 bl_label =
"opens ogremeshy in a subprocess" 2120 bl_options = {
'REGISTER'}
2121 preview = BoolProperty(name=
"preview", description=
"fast preview", default=
True)
2122 groups = BoolProperty(name=
"preview merge groups", description=
"use merge groups", default=
False)
2123 mesh = BoolProperty(name=
"update mesh", description=
"update mesh (disable for fast material preview", default=
True)
2126 def poll(cls, context):
2127 if context.active_object
and context.active_object.type
in (
'MESH',
'EMPTY')
and context.mode !=
'EDIT_MESH':
2128 if context.active_object.type ==
'EMPTY' and context.active_object.dupli_type !=
'GROUP':
2133 def execute(self, context):
2135 Report.messages.append(
'running %s' %CONFIG[
'OGRE_MESHY'])
2137 if sys.platform.startswith(
'linux'):
2140 if CONFIG[
'OGRE_MESHY'].endswith(
'.exe'):
2141 path =
'%s/.wine/drive_c/tmp' % os.environ[
'HOME']
2144 elif sys.platform.startswith(
'darwin')
or sys.platform.startswith(
'freebsd'):
2150 mgroup = merged =
None 2153 if context.active_object.type ==
'MESH':
2154 mat = context.active_object.active_material
2155 elif context.active_object.type ==
'EMPTY':
2157 for e
in context.selected_objects:
2158 if e.type !=
'EMPTY' and e.dupli_group:
continue 2161 for o
in grp.objects:
2162 if o.type==
'MESH': subs.append( o )
2164 m = merge_objects( subs, transform=e.matrix_world )
2167 merged = merge_objects( obs )
2168 umaterials = dot_mesh( merged, path=path, force_name=
'preview' )
2169 for o
in obs: context.scene.objects.unlink(o)
2172 for ob
in context.selected_objects:
2173 if ob.type ==
'MESH':
2174 for mat
in ob.data.materials:
2175 if mat
and mat
not in umaterials: umaterials.append( mat )
2178 mgroup = MeshMagick.get_merge_group( context.active_object )
2179 if not mgroup
and self.groups:
2180 group = get_merge_group( context.active_object )
2182 print(
'--------------- has merge group ---------------' )
2183 merged = merge_group( group )
2185 print(
'--------------- NO merge group ---------------' )
2186 elif len(context.selected_objects)>1
and context.selected_objects:
2187 merged = merge_objects( context.selected_objects )
2190 for ob
in mgroup.objects:
2191 nmats = dot_mesh( ob, path=path )
2193 if m
not in umaterials: umaterials.append( m )
2194 MeshMagick.merge( mgroup, path=path, force_name=
'preview' )
2196 umaterials = dot_mesh( merged, path=path, force_name=
'preview' )
2198 umaterials = dot_mesh( context.active_object, path=path, force_name=
'preview' )
2200 if mat
or umaterials:
2204 for umat
in umaterials:
2205 data += generate_material( umat, path=path, copy_programs=
True, touch_textures=
True )
2206 f=open( os.path.join( path,
'preview.material' ),
'wb' )
2207 f.write( bytes(data,
'utf-8') ); f.close()
2209 if merged: context.scene.objects.unlink( merged )
2211 if sys.platform.startswith(
'linux')
or sys.platform.startswith(
'darwin')
or sys.platform.startswith(
'freebsd'):
2212 if CONFIG[
'OGRE_MESHY'].endswith(
'.exe'):
2213 cmd = [
'wine', CONFIG[
'OGRE_MESHY'],
'c:\\tmp\\preview.mesh' ]
2215 cmd = [CONFIG[
'OGRE_MESHY'],
'/tmp/preview.mesh']
2218 subprocess.Popen(cmd)
2221 subprocess.Popen( [CONFIG[
'OGRE_MESHY'],
'C:\\tmp\\preview.mesh'] )
2230 tag = cls.__name__.split(
'_ogredoc_')[-1]
2231 cls.bl_label = tag.replace(
'_',
' ')
2232 _OGRE_DOCS_.append( cls )
2235 class INFO_MT_ogre_helper(bpy.types.Menu):
2236 bl_label =
'_overloaded_' 2238 def draw(self, context):
2239 layout = self.layout
2246 for line
in self.mydoc.splitlines():
2248 for ww
in wordwrap( line ): layout.label(text=ww)
2251 class INFO_MT_ogre_docs(bpy.types.Menu):
2252 bl_label =
"Ogre Help" 2254 def draw(self, context):
2255 layout = self.layout
2256 for cls
in _OGRE_DOCS_:
2257 layout.menu( cls.__name__ )
2260 layout.label(text=
'bug reports to: bhartsho@yahoo.com')
2262 class INFO_MT_ogre_shader_pass_attributes(bpy.types.Menu):
2263 bl_label =
"Shader-Pass" 2265 def draw(self, context):
2266 layout = self.layout
2267 for cls
in _OGRE_SHADER_REF_:
2268 layout.menu( cls.__name__ )
2270 class INFO_MT_ogre_shader_texture_attributes(bpy.types.Menu):
2271 bl_label =
"Shader-Texture" 2273 def draw(self, context):
2274 layout = self.layout
2275 for cls
in _OGRE_SHADER_REF_TEX_:
2276 layout.menu( cls.__name__ )
2279 class _ogredoc_Installing( INFO_MT_ogre_helper ):
2280 mydoc = _doc_installing_
2283 class _ogredoc_FAQ( INFO_MT_ogre_helper ):
2287 class _ogredoc_Animation_System( INFO_MT_ogre_helper ):
2289 Armature Animation System | OgreDotSkeleton 2291 1. select your armature and set a single keyframe on the object (loc,rot, or scl) 2292 . note, this step is just a hack for creating an action so you can then create an NLA track. 2293 . do not key in pose mode, unless you want to only export animation on the keyed bones. 2294 2. open the NLA, and convert the action into an NLA strip 2295 3. name the NLA strip(s) 2296 4. set the in and out frames for each strip ( the strip name becomes the Ogre track name ) 2299 The NLA strips can be blank, they are only used to define Ogre track names, and in and out frame ranges. You are free to animate the armature with constraints (no baking required), or you can used baked animation and motion capture. Blending that is driven by the NLA is also supported, if you don't want blending, put space between each strip. 2301 The OgreDotSkeleton (.skeleton) format supports multiple named tracks that can contain some or all of the bones of an armature. This feature can be exploited by a game engine for segmenting and animation blending. For example: lets say we want to animate the upper torso independently of the lower body while still using a single armature. This can be done by hijacking the NLA of the armature. 2303 Advanced NLA Hijacking (selected-bones-animation): 2304 . define an action and keyframe only the bones you want to 'group', ie. key all the upper torso bones 2305 . import the action into the NLA 2306 . name the strip (this becomes the track name in Ogre) 2307 . adjust the start and end frames of each strip 2308 ( you may use multiple NLA tracks, multiple strips per-track is ok, and strips may overlap in time ) 2313 class _ogredoc_Physics( INFO_MT_ogre_helper ):
2315 Ogre Dot Scene + BGE Physics 2316 extended format including external collision mesh, and BGE physics settings 2318 <entity name="..." meshFile="..." collisionFile="..." collisionPrim="..." [and all BGE physics attributes] /> 2321 collisionFile : sets path to .mesh that is used for collision (ignored if collisionPrim is set) 2322 collisionPrim : sets optimal collision type [ cube, sphere, capsule, cylinder ] 2323 *these collisions are static meshes, animated deforming meshes should give the user a warning that they have chosen a static mesh collision type with an object that has an armature 2325 Blender Collision Setup: 2326 1. If a mesh object has a child mesh with a name starting with 'collision', then the child becomes the collision mesh for the parent mesh. 2328 2. If 'Collision Bounds' game option is checked, the bounds type [box, sphere, etc] is used. This will override above rule. 2330 3. Instances (and instances generated by optimal array modifier) will share the same collision type of the first instance, you DO NOT need to set the collision type for each instance. 2335 class _ogredoc_Bugs( INFO_MT_ogre_helper ):
2338 . shape animation breaks when using modifiers that change the vertex count 2339 (Any modifier that changes the vertex count is bad with shape anim or armature anim) 2340 . never rename the nodes created by enabling Ogre-Material-Layers 2341 . never rename collision proxy meshes created by the Collision Panel 2342 . lighting in Tundra is not excatly the same as in Blender 2344 . only supports streaming transform of up to 10 objects selected objects 2345 . the 3D view must be shown at the time you open Tundra 2346 . the same 3D view must be visible to stream data to Tundra 2347 . only position and scale are updated, a bug on the Tundra side prevents rotation update 2348 . animation playback is broken if you rename your NLA strips after opening Tundra 2353 def _mesh_entity_helper( doc, ob, o ):
2355 o.setAttribute(
'mass', str(ob.game.mass))
2356 o.setAttribute(
'mass_radius', str(ob.game.radius))
2357 o.setAttribute(
'physics_type', ob.game.physics_type)
2358 o.setAttribute(
'actor', str(ob.game.use_actor))
2359 o.setAttribute(
'ghost', str(ob.game.use_ghost))
2360 o.setAttribute(
'velocity_min', str(ob.game.velocity_min))
2361 o.setAttribute(
'velocity_max', str(ob.game.velocity_max))
2362 o.setAttribute(
'lock_trans_x', str(ob.game.lock_location_x))
2363 o.setAttribute(
'lock_trans_y', str(ob.game.lock_location_y))
2364 o.setAttribute(
'lock_trans_z', str(ob.game.lock_location_z))
2365 o.setAttribute(
'lock_rot_x', str(ob.game.lock_rotation_x))
2366 o.setAttribute(
'lock_rot_y', str(ob.game.lock_rotation_y))
2367 o.setAttribute(
'lock_rot_z', str(ob.game.lock_rotation_z))
2368 o.setAttribute(
'anisotropic_friction', str(ob.game.use_anisotropic_friction))
2369 x,y,z = ob.game.friction_coefficients
2370 o.setAttribute(
'friction_x', str(x))
2371 o.setAttribute(
'friction_y', str(y))
2372 o.setAttribute(
'friction_z', str(z))
2373 o.setAttribute(
'damping_trans', str(ob.game.damping))
2374 o.setAttribute(
'damping_rot', str(ob.game.rotation_damping))
2375 o.setAttribute(
'inertia_tensor', str(ob.game.form_factor))
2379 for prop
in mesh.items():
2380 propname, propvalue = prop
2381 if not propname.startswith(
'_'):
2382 user = doc.createElement(
'user_data')
2383 o.appendChild( user )
2384 user.setAttribute(
'name', propname )
2385 user.setAttribute(
'value', str(propvalue) )
2386 user.setAttribute(
'type',
type(propvalue).__name__ )
2391 def get_lights_by_type( T ):
2393 for ob
in bpy.context.scene.objects:
2395 if ob.data.type==T: r.append( ob )
2398 class _TXML_(object):
2400 <component type="EC_Script" sync="1" name="myscript"> 2401 <attribute value="" name="Script ref"/> 2402 <attribute value="false" name="Run on load"/> 2403 <attribute value="0" name="Run mode"/> 2404 <attribute value="" name="Script application name"/> 2405 <attribute value="" name="Script class name"/> 2409 def create_tundra_document( self, context ):
2418 scn = doc.createElement(
'scene')
2419 doc.appendChild( scn )
2423 e = doc.createElement(
'entity' )
2424 doc.documentElement.appendChild( e )
2425 e.setAttribute(
'id', len(doc.documentElement.childNodes)+1 )
2427 c = doc.createElement(
'component' ); e.appendChild( c )
2428 c.setAttribute(
'type',
'EC_Script' )
2429 c.setAttribute(
'sync',
'1' )
2430 c.setAttribute(
'name',
'myscript' )
2432 a = doc.createElement(
'attribute'); c.appendChild( a )
2433 a.setAttribute(
'name',
'Script ref')
2436 a = doc.createElement(
'attribute'); c.appendChild( a )
2437 a.setAttribute(
'name',
'Run on load')
2438 a.setAttribute(
'value',
'true' )
2440 a = doc.createElement(
'attribute'); c.appendChild( a )
2441 a.setAttribute(
'name',
'Run mode')
2442 a.setAttribute(
'value',
'0' )
2444 a = doc.createElement(
'attribute'); c.appendChild( a )
2445 a.setAttribute(
'name',
'Script application name')
2446 a.setAttribute(
'value',
'blender2ogre' )
2450 if get_lights_by_type(
'SUN'):
2451 sun = get_lights_by_type(
'SUN')[0]
2452 if get_lights_by_type(
'HEMI'):
2453 hemi = get_lights_by_type(
'HEMI')[0]
2456 if bpy.context.scene.world.mist_settings.use_mist
or sun
or hemi:
2458 e = doc.createElement(
'entity' )
2459 doc.documentElement.appendChild( e )
2460 e.setAttribute(
'id', len(doc.documentElement.childNodes)+1 )
2463 c = doc.createElement(
'component' ); e.appendChild( c )
2464 c.setAttribute(
'type',
'EC_Fog' )
2465 c.setAttribute(
'sync',
'1' )
2466 c.setAttribute(
'name',
'Fog' )
2468 a = doc.createElement(
'attribute'); c.appendChild( a )
2469 a.setAttribute(
'name',
'Color')
2470 if bpy.context.scene.world.mist_settings.use_mist:
2471 A = bpy.context.scene.world.mist_settings.intensity
2472 R,G,B = bpy.context.scene.world.horizon_color
2473 a.setAttribute(
'value',
'%s %s %s %s'%(R,G,B,A))
2475 a.setAttribute(
'value',
'0.4 0.4 0.4 1.0')
2477 if bpy.context.scene.world.mist_settings.use_mist:
2478 mist = bpy.context.scene.world.mist_settings
2480 a = doc.createElement(
'attribute'); c.appendChild( a )
2481 a.setAttribute(
'name',
'Start distance')
2482 a.setAttribute(
'value', mist.start)
2484 a = doc.createElement(
'attribute'); c.appendChild( a )
2485 a.setAttribute(
'name',
'End distance')
2486 a.setAttribute(
'value', mist.start+mist.depth)
2488 a = doc.createElement(
'attribute'); c.appendChild( a )
2489 a.setAttribute(
'name',
'Exponential density')
2490 a.setAttribute(
'value', 0.001)
2493 c = doc.createElement(
'component' ); e.appendChild( c )
2494 c.setAttribute(
'type',
'EC_EnvironmentLight' )
2495 c.setAttribute(
'sync',
'1' )
2496 c.setAttribute(
'name',
'Environment Light' )
2498 a = doc.createElement(
'attribute'); c.appendChild( a )
2499 a.setAttribute(
'name',
'Sunlight color')
2501 R,G,B = sun.data.color
2502 a.setAttribute(
'value',
'%s %s %s 1' %(R,G,B))
2504 a.setAttribute(
'value',
'0 0 0 1')
2506 a = doc.createElement(
'attribute'); c.appendChild( a )
2507 a.setAttribute(
'name',
'Brightness')
2509 a.setAttribute(
'value', sun.data.energy*10)
2511 a.setAttribute(
'value',
'0')
2513 a = doc.createElement(
'attribute'); c.appendChild( a )
2514 a.setAttribute(
'name',
'Ambient light color')
2516 R,G,B = hemi.data.color * hemi.data.energy * 3.0
2520 a.setAttribute(
'value',
'%s %s %s 1' %(R,G,B))
2522 a.setAttribute(
'value',
'0 0 0 1')
2524 a = doc.createElement(
'attribute'); c.appendChild( a )
2525 a.setAttribute(
'name',
'Sunlight direction vector')
2526 a.setAttribute(
'value',
'-0.25 -1.0 -0.25')
2528 a = doc.createElement(
'attribute'); c.appendChild( a )
2529 a.setAttribute(
'name',
'Sunlight cast shadows')
2530 a.setAttribute(
'value',
'true')
2533 if context.scene.world.ogre_skyX:
2534 c = doc.createElement(
'component' ); e.appendChild( c )
2535 c.setAttribute(
'type',
'EC_SkyX' )
2536 c.setAttribute(
'sync',
'1' )
2537 c.setAttribute(
'name',
'SkyX' )
2539 a = doc.createElement(
'attribute'); a.setAttribute(
'name',
'Weather (volumetric clouds only)')
2541 context.scene.world.ogre_skyX_cloud_density_x,
2542 context.scene.world.ogre_skyX_cloud_density_y
2544 a.setAttribute(
'value',
'%s %s' %den)
2548 (
'time',
'Time multiplier'),
2549 (
'volumetric_clouds',
'Volumetric clouds'),
2550 (
'wind',
'Wind direction'),
2552 for bname, aname
in config:
2553 a = doc.createElement(
'attribute')
2554 a.setAttribute(
'name', aname)
2555 s = str( getattr(context.scene.world,
'ogre_skyX_'+bname) )
2556 a.setAttribute(
'value', s.lower())
2562 def tundra_entity( self, doc, ob, path='/tmp', collision_proxies=[], parent=None, matrix=None,visible=True ):
2563 assert not ob.subcollision
2567 matrix = ob.matrix_world.copy()
2578 objectname = clean_object_name(ob.name)
2579 print(
" Creating Tundra Enitity with ID", entityid)
2581 e = doc.createElement(
'entity' )
2582 doc.documentElement.appendChild( e )
2583 e.setAttribute(
'id', entityid)
2586 print (
" - EC_Name with", objectname)
2588 c = doc.createElement(
'component'); e.appendChild( c )
2589 c.setAttribute(
'type',
"EC_Name")
2590 c.setAttribute(
'sync',
'1')
2591 a = doc.createElement(
'attribute'); c.appendChild(a)
2592 a.setAttribute(
'name',
"name" )
2593 a.setAttribute(
'value', objectname )
2594 a = doc.createElement(
'attribute'); c.appendChild(a)
2595 a.setAttribute(
'name',
"description" )
2596 a.setAttribute(
'value',
"" )
2599 print (
" - EC_Placeable ")
2601 c = doc.createElement(
'component'); e.appendChild( c )
2602 c.setAttribute(
'type',
"EC_Placeable")
2603 c.setAttribute(
'sync',
'1')
2604 a = doc.createElement(
'attribute'); c.appendChild(a)
2605 a.setAttribute(
'name',
"Transform" )
2606 x,y,z =
swap(matrix.to_translation())
2607 loc =
'%6f,%6f,%6f' %(x,y,z)
2608 x,y,z =
swap(matrix.to_euler())
2609 x = math.degrees( x ); y = math.degrees( y ); z = math.degrees( z )
2610 if ob.type ==
'CAMERA':
2612 elif ob.type ==
'LAMP':
2614 rot =
'%6f,%6f,%6f' %(x,y,z)
2615 x,y,z =
swap(matrix.to_scale())
2616 scl =
'%6f,%6f,%6f' %(abs(x),abs(y),abs(z))
2617 a.setAttribute(
'value',
"%s,%s,%s" %(loc,rot,scl) )
2619 a = doc.createElement(
'attribute'); c.appendChild(a)
2620 a.setAttribute(
'name',
"Show bounding box" )
2621 a.setAttribute(
'value',
"false" )
2628 a = doc.createElement(
'attribute'); c.appendChild(a)
2629 a.setAttribute(
'name',
"Visible" )
2631 a.setAttribute(
'value',
'true')
2633 a.setAttribute(
'value',
'false')
2635 a = doc.createElement(
'attribute'); c.appendChild(a)
2636 a.setAttribute(
'name',
"Selection layer" )
2637 a.setAttribute(
'value', 1)
2644 a = doc.createElement(
'attribute'); c.appendChild(a)
2645 a.setAttribute(
'name',
"Parent entity ref" )
2646 a.setAttribute(
'value', parent)
2648 if ob.type !=
'MESH':
2649 c = doc.createElement(
'component'); e.appendChild( c )
2650 c.setAttribute(
'type',
'EC_Name')
2651 c.setAttribute(
'sync',
'1')
2652 a = doc.createElement(
'attribute'); c.appendChild(a)
2653 a.setAttribute(
'name',
"name" )
2654 a.setAttribute(
'value', objectname)
2657 if ob.type ==
'SPEAKER':
2658 print (
" - EC_Sound")
2659 c = doc.createElement(
'component'); e.appendChild( c )
2660 c.setAttribute(
'type',
'EC_Sound')
2661 c.setAttribute(
'sync',
'1')
2664 abspath = bpy.path.abspath( ob.data.sound.filepath )
2665 soundpath, soundfile = os.path.split( abspath )
2666 soundref =
"%s%s" % (proto, soundfile)
2667 print (
" Sounds ref:", soundref)
2668 a = doc.createElement(
'attribute'); c.appendChild(a)
2669 a.setAttribute(
'name',
'Sound ref' )
2670 a.setAttribute(
'value', soundref)
2671 if not os.path.isfile( os.path.join(path,soundfile) ):
2672 open( os.path.join(path,soundfile),
'wb' ).write( open(abspath,
'rb').read() )
2674 a = doc.createElement(
'attribute'); c.appendChild(a)
2675 a.setAttribute(
'name',
'Sound radius inner' )
2676 a.setAttribute(
'value', ob.data.cone_angle_inner)
2678 a = doc.createElement(
'attribute'); c.appendChild(a)
2679 a.setAttribute(
'name',
'Sound radius outer' )
2680 a.setAttribute(
'value', ob.data.cone_angle_outer)
2682 a = doc.createElement(
'attribute'); c.appendChild(a)
2683 a.setAttribute(
'name',
'Sound gain' )
2684 a.setAttribute(
'value', ob.data.volume)
2686 a = doc.createElement(
'attribute'); c.appendChild(a)
2687 a.setAttribute(
'name',
'Play on load' )
2688 if ob.data.play_on_load:
2689 a.setAttribute(
'value',
'true')
2691 a.setAttribute(
'value',
'false')
2693 a = doc.createElement(
'attribute'); c.appendChild(a)
2694 a.setAttribute(
'name',
'Loop sound' )
2696 a.setAttribute(
'value',
'true')
2698 a.setAttribute(
'value',
'false')
2700 a = doc.createElement(
'attribute'); c.appendChild(a)
2701 a.setAttribute(
'name',
'Spatial' )
2702 if ob.data.use_spatial:
2703 a.setAttribute(
'value',
'true')
2705 a.setAttribute(
'value',
'false')
2708 ''' todo: This is really not very helpful. Apps define 2709 camera logic in Tundra. By default you will have 2710 a freecamera to move around the scene etc. This created 2711 camera wont be activated except if a script does so. 2712 Best leave camera (creation) logic for the inworld apps. 2713 At least remove the default "export cameras" for txml. ''' 2714 if ob.type ==
'CAMERA':
2715 print (
" - EC_Camera")
2716 c = doc.createElement(
'component'); e.appendChild( c )
2717 c.setAttribute(
'type',
'EC_Camera')
2718 c.setAttribute(
'sync',
'1')
2719 a = doc.createElement(
'attribute'); c.appendChild(a)
2720 a.setAttribute(
'name',
"Up vector" )
2721 a.setAttribute(
'value',
'0.0 1.0 0.0')
2722 a = doc.createElement(
'attribute'); c.appendChild(a)
2723 a.setAttribute(
'name',
"Near plane" )
2724 a.setAttribute(
'value',
'0.01')
2725 a = doc.createElement(
'attribute'); c.appendChild(a)
2726 a.setAttribute(
'name',
"Far plane" )
2727 a.setAttribute(
'value',
'2000')
2728 a = doc.createElement(
'attribute'); c.appendChild(a)
2729 a.setAttribute(
'name',
"Vertical FOV" )
2730 a.setAttribute(
'value',
'45')
2731 a = doc.createElement(
'attribute'); c.appendChild(a)
2732 a.setAttribute(
'name',
"Aspect ratio" )
2733 a.setAttribute(
'value',
'')
2740 if ob.physics_mode !=
'NONE' or ob.collision_mode !=
'NONE':
2747 'TRIANGLE_MESH' : 4,
2752 com = doc.createElement(
'component'); e.appendChild( com )
2753 com.setAttribute(
'type',
'EC_RigidBody')
2754 com.setAttribute(
'sync',
'1')
2759 a = doc.createElement(
'attribute'); com.appendChild( a )
2760 a.setAttribute(
'name',
'Mass')
2761 if ob.physics_mode ==
'RIGID_BODY':
2762 a.setAttribute(
'value', ob.game.mass)
2764 a.setAttribute(
'value',
'0.0')
2766 SHAPE = a = doc.createElement(
'attribute'); com.appendChild( a )
2767 a.setAttribute(
'name',
'Shape type')
2768 a.setAttribute(
'value', TundraTypes[ ob.game.collision_bounds_type ] )
2770 print (
" - EC_RigidBody with shape type", TundraTypes[ob.game.collision_bounds_type])
2772 M = ob.game.collision_margin
2773 a = doc.createElement(
'attribute'); com.appendChild( a )
2774 a.setAttribute(
'name',
'Size')
2775 if ob.game.collision_bounds_type
in 'TRIANGLE_MESH CONVEX_HULL'.split():
2776 a.setAttribute(
'value',
'%s %s %s' %(1.0+M, 1.0+M, 1.0+M) )
2779 x,y,z =
swap(ob.dimensions)
2780 a.setAttribute(
'value',
'%s %s %s' %(abs(x)+M,abs(y)+M,abs(z)+M) )
2782 a = doc.createElement(
'attribute'); com.appendChild( a )
2783 a.setAttribute(
'name',
'Collision mesh ref')
2785 if ob.collision_mode ==
'DECIMATED':
2787 for child
in ob.children:
2788 if child.subcollision
and child.name.startswith(
'DECIMATED'):
2789 proxy = child;
break 2791 collisionref =
"%s_collision_%s.mesh" % (proto, proxy.data.name)
2792 a.setAttribute(
'value', collisionref)
2793 if proxy
not in collision_proxies:
2794 collision_proxies.append( proxy )
2796 print(
'[WARNINIG]: Collision proxy mesh not found' )
2798 elif ob.collision_mode ==
'TERRAIN':
2799 NTF = save_terrain_as_NTF( path, ob )
2800 SHAPE.setAttribute(
'value',
'5' )
2801 elif ob.type ==
'MESH':
2805 collisionref =
"%s%s.mesh" % (proto, clean_object_name(ob.data.name))
2806 a.setAttribute(
'value', collisionref)
2808 a = doc.createElement(
'attribute'); com.appendChild( a )
2809 a.setAttribute(
'name',
'Friction')
2811 a.setAttribute(
'value', ob.physics_friction)
2813 a = doc.createElement(
'attribute'); com.appendChild( a )
2814 a.setAttribute(
'name',
'Restitution')
2815 a.setAttribute(
'value', ob.physics_bounce)
2817 a = doc.createElement(
'attribute'); com.appendChild( a )
2818 a.setAttribute(
'name',
'Linear damping')
2819 a.setAttribute(
'value', ob.game.damping)
2821 a = doc.createElement(
'attribute'); com.appendChild( a )
2822 a.setAttribute(
'name',
'Angular damping')
2823 a.setAttribute(
'value', ob.game.rotation_damping)
2825 a = doc.createElement(
'attribute'); com.appendChild( a )
2826 a.setAttribute(
'name',
'Linear factor')
2827 a.setAttribute(
'value',
'1.0 1.0 1.0')
2829 a = doc.createElement(
'attribute'); com.appendChild( a )
2830 a.setAttribute(
'name',
'Angular factor')
2831 a.setAttribute(
'value',
'1.0 1.0 1.0')
2833 a = doc.createElement(
'attribute'); com.appendChild( a )
2834 a.setAttribute(
'name',
'Kinematic')
2835 a.setAttribute(
'value',
'false' )
2840 a = doc.createElement(
'attribute'); com.appendChild( a )
2841 a.setAttribute(
'name',
'Phantom')
2842 if ob.collision_mode ==
'NONE':
2843 a.setAttribute(
'value',
'true' )
2845 a.setAttribute(
'value',
'false' )
2847 a = doc.createElement(
'attribute'); com.appendChild( a )
2848 a.setAttribute(
'name',
'Draw Debug')
2849 a.setAttribute(
'value',
'false' )
2858 a = doc.createElement(
'attribute'); com.appendChild( a )
2859 a.setAttribute(
'name',
'Linear velocity')
2860 a.setAttribute(
'value',
'0.0 0.0 0.0')
2862 a = doc.createElement(
'attribute'); com.appendChild( a )
2863 a.setAttribute(
'name',
'Angular velocity')
2864 a.setAttribute(
'value',
'0.0 0.0 0.0')
2866 a = doc.createElement(
'attribute'); com.appendChild( a )
2867 a.setAttribute(
'name',
'Collision Layer')
2868 a.setAttribute(
'value', -1)
2870 a = doc.createElement(
'attribute'); com.appendChild( a )
2871 a.setAttribute(
'name',
'Collision Mask')
2872 a.setAttribute(
'value', -1)
2876 xp = NTF[
'xpatches']
2877 yp = NTF[
'ypatches']
2878 depth = NTF[
'depth']
2880 print (
" - EC_Terrain")
2881 com = doc.createElement(
'component'); e.appendChild( com )
2882 com.setAttribute(
'type',
'EC_Terrain')
2883 com.setAttribute(
'sync',
'1')
2885 a = doc.createElement(
'attribute'); com.appendChild( a )
2886 a.setAttribute(
'name',
'Transform')
2887 x,y,z = ob.dimensions
2893 trans =
'%s,%s,%s,' %(-xp/4, -depth, -yp/4)
2897 trans +=
'0,0,0,%s,%s,%s' %(nx,depth, ny)
2898 a.setAttribute(
'value', trans )
2900 a = doc.createElement(
'attribute'); com.appendChild( a )
2901 a.setAttribute(
'name',
'Grid Width')
2902 a.setAttribute(
'value', xp)
2904 a = doc.createElement(
'attribute'); com.appendChild( a )
2905 a.setAttribute(
'name',
'Grid Height')
2906 a.setAttribute(
'value', yp)
2908 a = doc.createElement(
'attribute'); com.appendChild( a )
2909 a.setAttribute(
'name',
'Tex. U scale')
2910 a.setAttribute(
'value', 1.0)
2912 a = doc.createElement(
'attribute'); com.appendChild( a )
2913 a.setAttribute(
'name',
'Tex. V scale')
2914 a.setAttribute(
'value', 1.0)
2916 a = doc.createElement(
'attribute'); com.appendChild( a )
2917 a.setAttribute(
'name',
'Material')
2918 a.setAttribute(
'value',
'')
2921 a = doc.createElement(
'attribute'); com.appendChild( a )
2922 a.setAttribute(
'name',
'Texture %s' %i)
2923 a.setAttribute(
'value',
'')
2927 heightmapref =
"%s%s" % (proto, NTF[
'name'])
2928 print (
" Heightmap ref:", heightmapref)
2929 a = doc.createElement(
'attribute'); com.appendChild( a )
2930 a.setAttribute(
'name',
'Heightmap')
2931 a.setAttribute(
'value', heightmapref )
2937 def tundra_mesh( self, e, ob, url, exported_meshes ):
2945 objectname = clean_object_name(ob.data.name)
2946 meshname =
"%s.mesh" % objectname
2947 meshref =
"%s%s.mesh" % (proto, objectname)
2949 print (
" - EC_Mesh")
2950 print (
" - Mesh ref:", meshref)
2953 murl = os.path.join( os.path.split(url)[0], meshname )
2954 exists = os.path.isfile( murl )
2955 if not exists
or (exists
and self.EX_MESH_OVERWRITE):
2956 if meshname
not in exported_meshes:
2957 exported_meshes.append( meshname )
2958 self.dot_mesh( ob, os.path.split(url)[0] )
2962 if ob.find_armature():
2963 print (
" - EC_AnimationController")
2964 c = doc.createElement(
'component'); e.appendChild( c )
2965 c.setAttribute(
'type',
"EC_AnimationController")
2966 c.setAttribute(
'sync',
'1')
2968 c = doc.createElement(
'component'); e.appendChild( c )
2969 c.setAttribute(
'type',
"EC_Mesh")
2970 c.setAttribute(
'sync',
'1')
2972 a = doc.createElement(
'attribute'); c.appendChild(a)
2973 a.setAttribute(
'name',
"Mesh ref" )
2974 a.setAttribute(
'value', meshref)
2976 a = doc.createElement(
'attribute'); c.appendChild(a)
2977 a.setAttribute(
'name',
"Mesh materials" )
2981 mymaterials = ob.data.materials
2982 if mymaterials
is not None and len(mymaterials) > 0:
2984 for mymat
in mymaterials:
2988 mymatstring += proto + material_name(mymat,
True) +
'.material;' 2989 mymatstring = mymatstring[:-1]
2990 a.setAttribute(
'value', mymatstring )
2993 a.setAttribute(
'value',
"" )
2995 if ob.find_armature():
2996 skeletonref =
"%s%s.skeleton" % (proto, clean_object_name(ob.data.name))
2997 print (
" Skeleton ref:", skeletonref)
2998 a = doc.createElement(
'attribute'); c.appendChild(a)
2999 a.setAttribute(
'name',
"Skeleton ref" )
3000 a.setAttribute(
'value', skeletonref)
3002 a = doc.createElement(
'attribute'); c.appendChild(a)
3003 a.setAttribute(
'name',
"Draw distance" )
3004 if ob.use_draw_distance:
3005 a.setAttribute(
'value', ob.draw_distance )
3007 a.setAttribute(
'value',
"0" )
3009 a = doc.createElement(
'attribute'); c.appendChild(a)
3010 a.setAttribute(
'name',
'Cast shadows' )
3012 a.setAttribute(
'value',
'true' )
3014 a.setAttribute(
'value',
'false' )
3017 def tundra_light( self, e, ob ):
3019 <component type="EC_Light" sync="1"> 3020 <attribute value="1" name="light type"/> 3021 <attribute value="1 1 1 1" name="diffuse color"/> 3022 <attribute value="1 1 1 1" name="specular color"/> 3023 <attribute value="true" name="cast shadows"/> 3024 <attribute value="29.9999828" name="light range"/> 3025 <attribute value="1" name="brightness"/> 3026 <attribute value="0" name="constant atten"/> 3027 <attribute value="1" name="linear atten"/> 3028 <attribute value="0" name="quadratic atten"/> 3029 <attribute value="30" name="light inner angle"/> 3030 <attribute value="40" name="light outer angle"/> 3034 if ob.data.type
not in 'POINT SPOT'.split():
3039 c = doc.createElement(
'component'); e.appendChild( c )
3040 c.setAttribute(
'type',
"EC_Light")
3041 c.setAttribute(
'sync',
'1')
3043 a = doc.createElement(
'attribute'); c.appendChild(a)
3044 a.setAttribute(
'name',
'light type' )
3045 if ob.data.type==
'POINT':
3046 a.setAttribute(
'value',
'0' )
3047 elif ob.data.type==
'SPOT':
3048 a.setAttribute(
'value',
'1' )
3051 R,G,B = ob.data.color
3052 a = doc.createElement(
'attribute'); c.appendChild(a)
3053 a.setAttribute(
'name',
'diffuse color' )
3054 if ob.data.use_diffuse:
3055 a.setAttribute(
'value',
'%s %s %s 1' %(R,G,B) )
3057 a.setAttribute(
'value',
'0 0 0 1' )
3059 a = doc.createElement(
'attribute'); c.appendChild(a)
3060 a.setAttribute(
'name',
'specular color' )
3061 if ob.data.use_specular:
3062 a.setAttribute(
'value',
'%s %s %s 1' %(R,G,B) )
3064 a.setAttribute(
'value',
'0 0 0 1' )
3066 a = doc.createElement(
'attribute'); c.appendChild(a)
3067 a.setAttribute(
'name',
'cast shadows' )
3068 if ob.data.type==
'HEMI':
3069 a.setAttribute(
'value',
'false' )
3070 elif ob.data.shadow_method !=
'NOSHADOW':
3071 a.setAttribute(
'value',
'true' )
3073 a.setAttribute(
'value',
'false' )
3075 a = doc.createElement(
'attribute'); c.appendChild(a)
3076 a.setAttribute(
'name',
'light range' )
3077 a.setAttribute(
'value', ob.data.distance*2 )
3079 a = doc.createElement(
'attribute'); c.appendChild(a)
3080 a.setAttribute(
'name',
'brightness' )
3081 a.setAttribute(
'value', ob.data.energy )
3083 a = doc.createElement(
'attribute'); c.appendChild(a)
3084 a.setAttribute(
'name',
'constant atten' )
3085 a.setAttribute(
'value',
'0' )
3087 a = doc.createElement(
'attribute'); c.appendChild(a)
3088 a.setAttribute(
'name',
'linear atten' )
3089 energy = ob.data.energy
3092 a.setAttribute(
'value', (1.0/energy)*0.25 )
3094 a = doc.createElement(
'attribute'); c.appendChild(a)
3095 a.setAttribute(
'name',
'quadratic atten' )
3096 a.setAttribute(
'value',
'0.0' )
3098 if ob.data.type==
'SPOT':
3099 outer = math.degrees(ob.data.spot_size) / 2.0
3100 inner = outer * (1.0-ob.data.spot_blend)
3102 a = doc.createElement(
'attribute'); c.appendChild(a)
3103 a.setAttribute(
'name',
'light inner angle' )
3104 a.setAttribute(
'value',
'%s'%inner )
3106 a = doc.createElement(
'attribute'); c.appendChild(a)
3107 a.setAttribute(
'name',
'light outer angle' )
3108 a.setAttribute(
'value',
'%s' %outer )
3112 invalid_chars =
'\/:*?"<>|' 3114 def clean_object_name(value):
3115 global invalid_chars
3116 for invalid_char
in invalid_chars:
3117 value = value.replace(invalid_char,
'_')
3118 value = value.replace(
' ',
'_')
3121 def clean_object_name_with_spaces(value):
3122 global invalid_chars
3123 for invalid_char
in invalid_chars:
3124 value = value.replace(invalid_char,
'_')
3127 last_export_filepath =
"" 3129 class _OgreCommonExport_(_TXML_):
3132 def poll(cls, context):
3133 if context.active_object
and context.mode !=
'EDIT_MESH':
3136 def invoke(self, context, event):
3140 global last_export_filepath
3141 if last_export_filepath ==
"":
3143 if self.filepath ==
"" and context.blend_data.filepath !=
"":
3144 path, name = os.path.split(context.blend_data.filepath)
3145 self.filepath = os.path.join(path, name.split(
'.')[0])
3146 if self.filepath ==
"":
3147 self.filepath =
"blender2ogre-export" 3148 if self.EXPORT_TYPE ==
"OGRE":
3149 self.filepath +=
".scene" 3150 elif self.EXPORT_TYPE ==
"REX":
3151 self.filepath +=
".txml" 3154 self.filepath = last_export_filepath
3157 if self.EXPORT_TYPE ==
"OGRE":
3158 self.filepath = self.filepath.replace(
".txml",
".scene")
3159 elif self.EXPORT_TYPE ==
"REX":
3160 self.filepath = self.filepath.replace(
".scene",
".txml")
3165 wm = context.window_manager
3166 fs = wm.fileselect_add(self)
3167 return {
'RUNNING_MODAL'}
3169 def execute(self, context):
3171 global last_export_filepath
3172 last_export_filepath = self.filepath
3175 self.ogre_export(self.filepath, context)
3178 def update_ui(self):
3179 self.EX_SWAP_AXIS = CONFIG[
'SWAP_AXIS']
3180 self.EX_SEP_MATS = CONFIG[
'SEP_MATS']
3181 self.EX_ONLY_DEFORMABLE_BONES = CONFIG[
'ONLY_DEFORMABLE_BONES']
3182 self.EX_ONLY_KEYFRAMED_BONES = CONFIG[
'ONLY_KEYFRAMED_BONES']
3183 self.EX_OGRE_INHERIT_SCALE = CONFIG[
'OGRE_INHERIT_SCALE']
3184 self.EX_SCENE = CONFIG[
'SCENE']
3185 self.EX_EXPORT_HIDDEN = CONFIG[
'EXPORT_HIDDEN']
3186 self.EX_SELONLY = CONFIG[
'SELONLY']
3187 self.EX_FORCE_CAMERA = CONFIG[
'FORCE_CAMERA']
3188 self.EX_FORCE_LAMPS = CONFIG[
'FORCE_LAMPS']
3189 self.EX_MESH = CONFIG[
'MESH']
3190 self.EX_MESH_OVERWRITE = CONFIG[
'MESH_OVERWRITE']
3191 self.EX_ARM_ANIM = CONFIG[
'ARM_ANIM']
3192 self.EX_SHAPE_ANIM = CONFIG[
'SHAPE_ANIM']
3193 self.EX_TRIM_BONE_WEIGHTS = CONFIG[
'TRIM_BONE_WEIGHTS']
3194 self.EX_ARRAY = CONFIG[
'ARRAY']
3195 self.EX_MATERIALS = CONFIG[
'MATERIALS']
3196 self.EX_FORCE_IMAGE_FORMAT = CONFIG[
'FORCE_IMAGE_FORMAT']
3197 self.EX_DDS_MIPS = CONFIG[
'DDS_MIPS']
3198 self.EX_COPY_SHADER_PROGRAMS = CONFIG[
'COPY_SHADER_PROGRAMS']
3199 self.EX_lodLevels = CONFIG[
'lodLevels']
3200 self.EX_lodDistance = CONFIG[
'lodDistance']
3201 self.EX_lodPercent = CONFIG[
'lodPercent']
3202 self.EX_nuextremityPoints = CONFIG[
'nuextremityPoints']
3203 self.EX_generateEdgeLists = CONFIG[
'generateEdgeLists']
3204 self.EX_generateTangents = CONFIG[
'generateTangents']
3205 self.EX_tangentSemantic = CONFIG[
'tangentSemantic']
3206 self.EX_tangentUseParity = CONFIG[
'tangentUseParity']
3207 self.EX_tangentSplitMirrored = CONFIG[
'tangentSplitMirrored']
3208 self.EX_tangentSplitRotated = CONFIG[
'tangentSplitRotated']
3209 self.EX_reorganiseBuffers = CONFIG[
'reorganiseBuffers']
3210 self.EX_optimiseAnimations = CONFIG[
'optimiseAnimations']
3213 EX_SWAP_AXIS = EnumProperty(
3216 description=
'axis swapping mode',
3217 default= CONFIG[
'SWAP_AXIS'])
3218 EX_SEP_MATS = BoolProperty(
3219 name=
"Separate Materials",
3220 description=
"exports a .material for each material (rather than putting all materials in a single .material file)",
3221 default=CONFIG[
'SEP_MATS'])
3222 EX_ONLY_DEFORMABLE_BONES = BoolProperty(
3223 name=
"Only Deformable Bones",
3224 description=
"only exports bones that are deformable. Useful for hiding IK-Bones used in Blender. Note: Any bone with deformable children/descendants will be output as well.",
3225 default=CONFIG[
'ONLY_DEFORMABLE_BONES'])
3226 EX_ONLY_KEYFRAMED_BONES = BoolProperty(
3227 name=
"Only Keyframed Bones",
3228 description=
"only exports bones that have been keyframed for a given animation. Useful to limit the set of bones on a per-animation basis.",
3229 default=CONFIG[
'ONLY_KEYFRAMED_BONES'])
3230 EX_OGRE_INHERIT_SCALE = BoolProperty(
3231 name=
"OGRE inherit scale",
3232 description=
"whether the OGRE bones have the 'inherit scale' flag on. If the animation has scale in it, the exported animation needs to be adjusted to account for the state of the inherit-scale flag in OGRE.",
3233 default=CONFIG[
'OGRE_INHERIT_SCALE'])
3234 EX_SCENE = BoolProperty(
3235 name=
"Export Scene",
3236 description=
"export current scene (OgreDotScene xml)",
3237 default=CONFIG[
'SCENE'])
3238 EX_SELONLY = BoolProperty(
3239 name=
"Export Selected Only",
3240 description=
"export selected",
3241 default=CONFIG[
'SELONLY'])
3242 EX_EXPORT_HIDDEN = BoolProperty(
3243 name=
"Export Hidden Also",
3244 description=
"Export hidden meshes in addition to visible ones. Turn off to avoid exporting hidden stuff.",
3245 default=CONFIG[
'EXPORT_HIDDEN'])
3246 EX_FORCE_CAMERA = BoolProperty(
3247 name=
"Force Camera",
3248 description=
"export active camera",
3249 default=CONFIG[
'FORCE_CAMERA'])
3250 EX_FORCE_LAMPS = BoolProperty(
3252 description=
"export all lamps",
3253 default=CONFIG[
'FORCE_LAMPS'])
3254 EX_MESH = BoolProperty(
3255 name=
"Export Meshes",
3256 description=
"export meshes",
3257 default=CONFIG[
'MESH'])
3258 EX_MESH_OVERWRITE = BoolProperty(
3259 name=
"Export Meshes (overwrite)",
3260 description=
"export meshes (overwrite existing files)",
3261 default=CONFIG[
'MESH_OVERWRITE'])
3262 EX_ARM_ANIM = BoolProperty(
3263 name=
"Armature Animation",
3264 description=
"export armature animations - updates the .skeleton file",
3265 default=CONFIG[
'ARM_ANIM'])
3266 EX_SHAPE_ANIM = BoolProperty(
3267 name=
"Shape Animation",
3268 description=
"export shape animations - updates the .mesh file",
3269 default=CONFIG[
'SHAPE_ANIM'])
3270 EX_TRIM_BONE_WEIGHTS = FloatProperty(
3271 name=
"Trim Weights",
3272 description=
"ignore bone weights below this value (Ogre supports 4 bones per vertex)",
3273 min=0.0, max=0.5, default=CONFIG[
'TRIM_BONE_WEIGHTS'] )
3274 EX_ARRAY = BoolProperty(
3275 name=
"Optimize Arrays",
3276 description=
"optimize array modifiers as instances (constant offset only)",
3277 default=CONFIG[
'ARRAY'])
3278 EX_MATERIALS = BoolProperty(
3279 name=
"Export Materials",
3280 description=
"exports .material script",
3281 default=CONFIG[
'MATERIALS'])
3282 EX_FORCE_IMAGE_FORMAT = EnumProperty(
3283 items=_IMAGE_FORMATS,
3284 name=
'Convert Images',
3285 description=
'convert all textures to format',
3286 default=CONFIG[
'FORCE_IMAGE_FORMAT'] )
3287 EX_DDS_MIPS = IntProperty(
3289 description=
"number of mip maps (DDS)",
3291 default=CONFIG[
'DDS_MIPS'])
3294 EX_lodLevels = IntProperty(
3296 description=
"MESH number of LOD levels",
3298 default=CONFIG[
'lodLevels'])
3299 EX_lodDistance = IntProperty(
3300 name=
"LOD Distance",
3301 description=
"MESH distance increment to reduce LOD",
3302 min=0, max=2000, default=CONFIG[
'lodDistance'])
3303 EX_lodPercent = IntProperty(
3304 name=
"LOD Percentage",
3305 description=
"LOD percentage reduction",
3307 default=CONFIG[
'lodPercent'])
3308 EX_nuextremityPoints = IntProperty(
3309 name=
"Extremity Points",
3310 description=
"MESH Extremity Points",
3312 default=CONFIG[
'nuextremityPoints'])
3313 EX_generateEdgeLists = BoolProperty(
3315 description=
"MESH generate edge lists (for stencil shadows)",
3316 default=CONFIG[
'generateEdgeLists'])
3317 EX_generateTangents = BoolProperty(
3319 description=
"MESH generate tangents",
3320 default=CONFIG[
'generateTangents'])
3321 EX_tangentSemantic = StringProperty(
3322 name=
"Tangent Semantic",
3323 description=
"MESH tangent semantic - can be 'uvw' or 'tangent'",
3325 default=CONFIG[
'tangentSemantic'])
3326 EX_tangentUseParity = IntProperty(
3327 name=
"Tangent Parity",
3328 description=
"MESH tangent use parity",
3330 default=CONFIG[
'tangentUseParity'])
3331 EX_tangentSplitMirrored = BoolProperty(
3332 name=
"Tangent Split Mirrored",
3333 description=
"MESH split mirrored tangents",
3334 default=CONFIG[
'tangentSplitMirrored'])
3335 EX_tangentSplitRotated = BoolProperty(
3336 name=
"Tangent Split Rotated",
3337 description=
"MESH split rotated tangents",
3338 default=CONFIG[
'tangentSplitRotated'])
3339 EX_reorganiseBuffers = BoolProperty(
3340 name=
"Reorganise Buffers",
3341 description=
"MESH reorganise vertex buffers",
3342 default=CONFIG[
'reorganiseBuffers'])
3343 EX_optimiseAnimations = BoolProperty(
3344 name=
"Optimize Animations",
3345 description=
"MESH optimize animations",
3346 default=CONFIG[
'optimiseAnimations'])
3347 EX_COPY_SHADER_PROGRAMS = BoolProperty(
3348 name=
"copy shader programs",
3349 description=
"when using script inheritance copy the source shader programs to the output path",
3350 default=CONFIG[
'COPY_SHADER_PROGRAMS'])
3353 filepath = StringProperty(
3355 description=
"Filepath used for exporting file",
3356 maxlen=1024, default=
"",
3357 subtype=
'FILE_PATH')
3359 def dot_material( self, meshes, path='/tmp', mat_file_name='SceneMaterial'):
3363 if len(ob.data.materials):
3364 for mat
in ob.data.materials:
3369 print(
'WARNING: no materials, not writting .material script');
return []
3371 M = MISSING_MATERIAL +
'\n' 3375 Report.materials.append( material_name(mat) )
3376 if CONFIG[
'COPY_SHADER_PROGRAMS']:
3377 data = generate_material( mat, path=path, copy_programs=
True, touch_textures=CONFIG[
'TOUCH_TEXTURES'] )
3379 data = generate_material( mat, path=path, touch_textures=CONFIG[
'TOUCH_TEXTURES'] )
3383 if self.EX_SEP_MATS:
3384 url = self.dot_material_write_separate( mat, data, path )
3385 material_files.append(url)
3388 if not self.EX_SEP_MATS:
3390 url = os.path.join(path,
'%s.material' % mat_file_name)
3391 f = open( url,
'wb' ); f.write( bytes(M,
'utf-8') ); f.close()
3392 print(
' - Created material:', url)
3393 material_files.append( url )
3394 except Exception
as e:
3395 show_dialog(
"Invalid material object name: " + mat_file_name)
3397 return material_files
3399 def dot_material_write_separate( self, mat, data, path = '/tmp' ):
3401 clean_filename = clean_object_name(mat.name);
3402 url = os.path.join(path,
'%s.material' % clean_filename)
3403 f = open(url,
'wb'); f.write( bytes(data,
'utf-8') ); f.close()
3404 print(
' - Exported Material:', url)
3406 except Exception
as e:
3407 show_dialog(
"Invalid material object name: " + clean_filename)
3410 def dot_mesh( self, ob, path='/tmp', force_name=None, ignore_shape_animation=False ):
3411 dot_mesh( ob, path, force_name, ignore_shape_animation=
False )
3413 def ogre_export(self, url, context, force_material_update=[]):
3418 for name
in dir(self):
3419 if name.startswith(
'EX_'):
3420 CONFIG[ name[3:] ] = getattr(self,name)
3424 print(
"Processing Scene")
3425 prefix = url.split(
'.')[0]
3426 path = os.path.split(url)[0]
3431 invalidnamewarnings = []
3432 for ob
in bpy.context.scene.objects:
3435 if not self.EX_EXPORT_HIDDEN
and ob.hide:
3437 if self.EX_SELONLY
and not ob.select:
3438 if ob.type ==
'CAMERA' and self.EX_FORCE_CAMERA:
3440 elif ob.type ==
'LAMP' and self.EX_FORCE_LAMPS:
3444 if ob.type ==
'EMPTY' and ob.dupli_group
and ob.dupli_type ==
'GROUP':
3445 linkedgroups.append(ob)
3449 cleanname = clean_object_name(ob.name)
3450 cleannamespaces = clean_object_name_with_spaces(ob.name)
3451 if cleanname != ob.name:
3452 if cleannamespaces != ob.name:
3453 invalidnamewarnings.append(ob.name +
" -> " + cleanname)
3457 if len(invalidnamewarnings) > 0:
3458 print (
"[Warning]: Following object names have invalid characters for creating files. They will be automatically converted.")
3459 for namewarning
in invalidnamewarnings:
3460 Report.warnings.append(
"Auto correcting object name: " + namewarning)
3461 print (
" - ", namewarning)
3465 for e
in linkedgroups:
3468 for o
in grp.objects:
3471 elif o.type ==
'EMPTY' and o.dupli_group
and o.dupli_type ==
'GROUP':
3473 for oo
in o.dupli_group.objects:
3476 elif oo.type ==
'EMPTY' and oo.dupli_group
and oo.dupli_type ==
'GROUP':
3478 for ooo
in oo.dupli_group.objects:
3479 if ooo.type==
'MESH':
3482 m = merge_objects( sss, name=oo.name, transform=oo.matrix_world )
3486 m = merge_objects( ss, name=o.name, transform=o.matrix_world )
3490 m = merge_objects( subs, name=e.name, transform=e.matrix_world )
3498 group = get_merge_group( ob )
3500 for member
in group.objects:
3501 if member
not in mobjects: mobjects.append( member )
3502 if group
not in mgroups: mgroups.append( group )
3503 for rem
in mobjects:
3504 if rem
in objects: objects.remove( rem )
3506 for group
in mgroups:
3507 merged = merge_group( group )
3508 objects.append( merged )
3509 temps.append( merged )
3512 def _flatten( _c, _f ):
3513 if _c.parent
in objects: _f.append( _c.parent )
3514 if _c.parent: _flatten( _c.parent, _f )
3515 else: _f.append( _c )
3522 _flatten( ob, flat )
3524 if root
not in roots:
3529 mesh_collision_prims = {}
3530 mesh_collision_files = {}
3533 exported_meshes = []
3535 if self.EX_MATERIALS:
3536 print (
" Processing Materials")
3537 material_file_name_base = os.path.split(url)[1].replace(
'.scene',
'').replace(
'.txml',
'')
3538 material_files = self.dot_material(meshes + force_material_update, path, material_file_name_base)
3543 if self.EXPORT_TYPE ==
'REX':
3544 rex = self.create_tundra_document(context)
3547 print(
" Processing %s [%s]" % (ob.name, ob.type))
3552 if ob.type ==
'MESH':
3553 ob.data.update(calc_tessface=
True)
3556 if ob.type ==
'LAMP':
3557 TE = self.tundra_entity(rex, ob, path=path, collision_proxies=proxies)
3558 self.tundra_light( TE, ob )
3560 elif ob.type ==
'SPEAKER':
3561 TE = self.tundra_entity(rex, ob, path=path, collision_proxies=proxies)
3563 elif ob.type ==
'MESH' and len(ob.data.tessfaces):
3564 if ob.modifiers
and ob.modifiers[0].type==
'MULTIRES' and ob.use_multires_lod:
3565 mod = ob.modifiers[0]
3567 dataname = ob.data.name
3569 TE = self.tundra_entity(rex, ob, path=path, collision_proxies=proxies)
3571 for level
in range( mod.total_levels+1 ):
3574 ob.name =
'%s.LOD%s' %(basename,level)
3575 ob.data.name =
'%s.LOD%s' %(dataname,level)
3576 TE = self.tundra_entity(
3577 rex, ob, path=path, collision_proxies=proxies, parent=basename,
3578 matrix=mathutils.Matrix(), visible=
False 3580 self.tundra_mesh( TE, ob, url, exported_meshes )
3584 ob.data.name = dataname
3586 TE = self.tundra_entity( rex, ob, path=path, collision_proxies=proxies )
3587 self.tundra_mesh( TE, ob, url, exported_meshes )
3590 for proxy
in proxies:
3593 path=os.path.split(url)[0],
3594 force_name=
'_collision_%s' %proxy.data.name
3598 if not url.endswith(
'.txml'):
3600 data = rex.toprettyxml()
3601 f = open( url,
'wb' ); f.write( bytes(data,
'utf-8') ); f.close()
3602 print(
' Exported Tundra Scene:', url)
3605 elif self.EXPORT_TYPE ==
'OGRE':
3606 doc = self.create_ogre_document( context, material_files )
3609 print(
' - Exporting root node:', root.name)
3614 exported_meshes = exported_meshes,
3616 mesh_collision_prims = mesh_collision_prims,
3617 mesh_collision_files = mesh_collision_files,
3620 xmlparent = doc._scene_nodes
3624 if not url.endswith(
'.scene'):
3626 data = doc.toprettyxml()
3627 f = open( url,
'wb' ); f.write( bytes(data,
'utf-8') ); f.close()
3628 print(
' Exported Ogre Scene:', url)
3631 context.scene.objects.unlink( ob )
3632 bpy.ops.wm.call_menu(name=
'MiniReport')
3639 def create_ogre_document(self, context, material_files=[] ):
3642 scn = doc.createElement(
'scene'); doc.appendChild( scn )
3643 scn.setAttribute(
'export_time', str(now))
3644 scn.setAttribute(
'formatVersion',
'1.0.1')
3645 bscn = bpy.context.scene
3647 if '_previous_export_time_' in bscn.keys():
3648 scn.setAttribute(
'previous_export_time', str(bscn[
'_previous_export_time_']))
3650 scn.setAttribute(
'previous_export_time',
'0')
3651 bscn[
'_previous_export_time_' ] = now
3652 scn.setAttribute(
'exported_by', getpass.getuser())
3654 nodes = doc.createElement(
'nodes')
3655 doc._scene_nodes = nodes
3656 extern = doc.createElement(
'externals')
3657 environ = doc.createElement(
'environment')
3658 for n
in (nodes,extern,environ):
3659 scn.appendChild( n )
3662 for url
in material_files:
3663 item = doc.createElement(
'item'); extern.appendChild( item )
3664 item.setAttribute(
'type',
'material')
3665 a = doc.createElement(
'file'); item.appendChild( a )
3666 a.setAttribute(
'name', url)
3669 world = context.scene.world
3671 _c = {
'colourAmbient':world.ambient_color,
'colourBackground':world.horizon_color,
'colourDiffuse':world.horizon_color}
3673 a = doc.createElement(ctag); environ.appendChild( a )
3675 a.setAttribute(
'r', '%s'%color.r)
3676 a.setAttribute(
'g',
'%s'%color.g)
3677 a.setAttribute(
'b',
'%s'%color.b)
3679 if world
and world.mist_settings.use_mist:
3680 a = doc.createElement(
'fog'); environ.appendChild( a )
3681 a.setAttribute(
'linearStart',
'%s'%world.mist_settings.start )
3682 mist_falloff = world.mist_settings.falloff
3683 if mist_falloff ==
'QUADRATIC': a.setAttribute(
'mode',
'exp')
3684 elif mist_falloff ==
'LINEAR': a.setAttribute(
'mode',
'linear')
3685 else: a.setAttribute(
'mode',
'exp2')
3687 a.setAttribute(
'linearEnd',
'%s' %(world.mist_settings.start+world.mist_settings.depth))
3688 a.setAttribute(
'expDensity', world.mist_settings.intensity)
3689 a.setAttribute(
'colourR', world.horizon_color.r)
3690 a.setAttribute(
'colourG', world.horizon_color.g)
3691 a.setAttribute(
'colourB', world.horizon_color.b)
3696 def _node_export( self, ob, url='', doc=None, rex=None, exported_meshes=[], meshes=[], mesh_collision_prims={}, mesh_collision_files={}, prefix='', objects=[], xmlparent=None ):
3697 o = _ogre_node_helper( doc, ob, objects )
3698 xmlparent.appendChild(o)
3701 for prop
in ob.items():
3702 propname, propvalue = prop
3703 if not propname.startswith(
'_'):
3704 user = doc.createElement(
'user_data')
3705 o.appendChild( user )
3706 user.setAttribute(
'name', propname )
3707 user.setAttribute(
'value', str(propvalue) )
3708 user.setAttribute(
'type',
type(propvalue).__name__ )
3711 for prop
in ob.game.properties:
3712 e = doc.createElement(
'user_data' )
3714 e.setAttribute(
'name', prop.name)
3715 e.setAttribute(
'value', str(prop.value))
3716 e.setAttribute(
'type',
type(prop.value).__name__)
3720 game = doc.createElement(
'game')
3721 o.appendChild( game )
3722 sens = doc.createElement(
'sensors')
3723 game.appendChild( sens )
3724 acts = doc.createElement(
'actuators')
3725 game.appendChild( acts )
3726 for sen
in ob.game.sensors:
3727 sens.appendChild( WrapSensor(sen).xml(doc) )
3728 for act
in ob.game.actuators:
3729 acts.appendChild( WrapActuator(act).xml(doc) )
3731 if ob.type ==
'MESH':
3732 ob.data.update(calc_tessface=
True)
3734 if ob.type ==
'MESH' and len(ob.data.tessfaces):
3735 collisionFile =
None 3736 collisionPrim =
None 3737 if ob.data.name
in mesh_collision_prims:
3738 collisionPrim = mesh_collision_prims[ ob.data.name ]
3739 if ob.data.name
in mesh_collision_files:
3740 collisionFile = mesh_collision_files[ ob.data.name ]
3742 e = doc.createElement(
'entity')
3743 o.appendChild(e); e.setAttribute(
'name', ob.data.name)
3745 e.setAttribute(
'meshFile',
'%s%s.mesh' %(prefix,ob.data.name) )
3747 if not collisionPrim
and not collisionFile:
3748 if ob.game.use_collision_bounds:
3749 collisionPrim = ob.game.collision_bounds_type.lower()
3750 mesh_collision_prims[ ob.data.name ] = collisionPrim
3752 for child
in ob.children:
3753 if child.subcollision
and child.name.startswith(
'DECIMATE'):
3754 collisionFile =
'%s_collision_%s.mesh' %(prefix,ob.data.name)
3757 mesh_collision_files[ ob.data.name ] = collisionFile
3760 path=os.path.split(url)[0],
3761 force_name=
'_collision_%s' %ob.data.name
3765 e.setAttribute(
'collisionPrim', collisionPrim )
3767 e.setAttribute(
'collisionFile', collisionFile )
3769 _mesh_entity_helper( doc, ob, e )
3772 murl = os.path.join( os.path.split(url)[0],
'%s.mesh'%ob.data.name )
3773 exists = os.path.isfile( murl )
3774 if not exists
or (exists
and self.EX_MESH_OVERWRITE):
3775 if ob.data.name
not in exported_meshes:
3776 exported_meshes.append( ob.data.name )
3777 self.dot_mesh( ob, os.path.split(url)[0] )
3780 vecs = [ ob.matrix_world.to_translation() ]
3781 for mod
in ob.modifiers:
3782 if mod.type ==
'ARRAY':
3783 if mod.fit_type !=
'FIXED_COUNT':
3784 print(
'WARNING: unsupport array-modifier type->', mod.fit_type )
3786 if not mod.use_constant_offset:
3787 print(
'WARNING: unsupport array-modifier mode, must be "constant offset" type' )
3793 for i
in range( mod.count-1 ):
3794 v = prev + mod.constant_offset_displace
3796 ao = _ogre_node_helper( doc, ob, objects, prefix=
'_array_%s_'%len(vecs+newvecs), pos=v )
3797 xmlparent.appendChild(ao)
3799 e = doc.createElement(
'entity')
3800 ao.appendChild(e); e.setAttribute(
'name', ob.data.name)
3803 e.setAttribute(
'meshFile',
'%s.mesh' %ob.data.name)
3805 if collisionPrim: e.setAttribute(
'collisionPrim', collisionPrim )
3806 elif collisionFile: e.setAttribute(
'collisionFile', collisionFile )
3809 elif ob.type ==
'CAMERA':
3810 Report.cameras.append( ob.name )
3811 c = doc.createElement(
'camera')
3812 o.appendChild(c); c.setAttribute(
'name', ob.data.name)
3813 aspx = bpy.context.scene.render.pixel_aspect_x
3814 aspy = bpy.context.scene.render.pixel_aspect_y
3815 sx = bpy.context.scene.render.resolution_x
3816 sy = bpy.context.scene.render.resolution_y
3818 if (sx*aspx > sy*aspy):
3819 fovY = 2*math.atan(sy*aspy*16.0/(ob.data.lens*sx*aspx))
3821 fovY = 2*math.atan(16.0/ob.data.lens)
3823 fov = math.radians( fovY*180.0/math.pi )
3824 c.setAttribute(
'fov',
'%s'%fov)
3825 c.setAttribute(
'projectionType',
"perspective")
3826 a = doc.createElement(
'clipping'); c.appendChild( a )
3827 a.setAttribute(
'nearPlaneDist',
'%s' %ob.data.clip_start)
3828 a.setAttribute(
'farPlaneDist',
'%s' %ob.data.clip_end)
3829 a.setAttribute(
'near',
'%s' %ob.data.clip_start)
3830 a.setAttribute(
'far',
'%s' %ob.data.clip_end)
3832 elif ob.type ==
'LAMP' and ob.data.type
in 'POINT SPOT SUN'.split():
3833 Report.lights.append( ob.name )
3834 l = doc.createElement(
'light')
3837 mat = get_parent_matrix(ob, objects).inverted() * ob.matrix_world
3839 p = doc.createElement(
'position')
3841 v =
swap( ob.matrix_world.to_translation() )
3842 p.setAttribute(
'x',
'%6f'%v.x)
3843 p.setAttribute(
'y',
'%6f'%v.y)
3844 p.setAttribute(
'z',
'%6f'%v.z)
3846 if ob.data.type ==
'POINT':
3847 l.setAttribute(
'type',
'point')
3848 elif ob.data.type ==
'SPOT':
3849 l.setAttribute(
'type',
'spot')
3850 elif ob.data.type ==
'SUN':
3851 l.setAttribute(
'type',
'directional')
3853 l.setAttribute(
'name', ob.name )
3854 l.setAttribute(
'powerScale', str(ob.data.energy))
3856 a = doc.createElement(
'lightAttenuation'); l.appendChild( a )
3857 a.setAttribute(
'range',
'5000' )
3858 a.setAttribute(
'constant',
'1.0')
3859 a.setAttribute(
'linear',
'%s'%(1.0/ob.data.distance))
3860 a.setAttribute(
'quadratic',
'0.0')
3862 if ob.data.type
in (
'SPOT',
'SUN'):
3863 vector =
swap(mathutils.Euler.to_matrix(ob.rotation_euler)[2])
3864 a = doc.createElement(
'direction')
3866 a.setAttribute(
'x',str(round(-vector[0],3)))
3867 a.setAttribute(
'y',str(round(-vector[1],3)))
3868 a.setAttribute(
'z',str(round(-vector[2],3)))
3870 if ob.data.type ==
'SPOT':
3871 a = doc.createElement(
'spotLightRange')
3873 a.setAttribute(
'inner',str( ob.data.spot_size*(1.0-ob.data.spot_blend) ))
3874 a.setAttribute(
'outer',str(ob.data.spot_size))
3875 a.setAttribute(
'falloff',
'1.0')
3877 if ob.data.use_diffuse:
3878 a = doc.createElement(
'colourDiffuse'); l.appendChild( a )
3879 a.setAttribute(
'r', '%s'%ob.data.color.r)
3880 a.setAttribute(
'g',
'%s'%ob.data.color.g)
3881 a.setAttribute(
'b',
'%s'%ob.data.color.b)
3883 if ob.data.use_specular:
3884 a = doc.createElement(
'colourSpecular'); l.appendChild( a )
3885 a.setAttribute(
'r', '%s'%ob.data.color.r)
3886 a.setAttribute(
'g',
'%s'%ob.data.color.g)
3887 a.setAttribute(
'b',
'%s'%ob.data.color.b)
3889 if ob.data.type !=
'HEMI':
3890 if ob.data.shadow_method !=
'NOSHADOW':
3891 a = doc.createElement(
'colourShadow');l.appendChild( a )
3892 a.setAttribute(
'r', '%s'%ob.data.color.r)
3893 a.setAttribute(
'g',
'%s'%ob.data.color.g)
3894 a.setAttribute(
'b',
'%s'%ob.data.color.b)
3895 l.setAttribute(
'shadow',
'true')
3897 for child
in ob.children:
3898 self._node_export( child,
3899 url = url, doc = doc, rex = rex,
3900 exported_meshes = exported_meshes,
3902 mesh_collision_prims = mesh_collision_prims,
3903 mesh_collision_files = mesh_collision_files,
3911 class INFO_OT_createOgreExport(bpy.types.Operator, _OgreCommonExport_):
3912 '''Export Ogre Scene''' 3913 bl_idname =
"ogre.export" 3914 bl_label =
"Export Ogre" 3915 bl_options = {
'REGISTER'}
3918 EX_SWAP_AXIS = EnumProperty(
3921 description=
'axis swapping mode',
3922 default= CONFIG[
'SWAP_AXIS'])
3923 EX_SEP_MATS = BoolProperty(
3924 name=
"Separate Materials",
3925 description=
"exports a .material for each material (rather than putting all materials in a single .material file)",
3926 default=CONFIG[
'SEP_MATS'])
3927 EX_ONLY_DEFORMABLE_BONES = BoolProperty(
3928 name=
"Only Deformable Bones",
3929 description=
"only exports bones that are deformable. Useful for hiding IK-Bones used in Blender. Note: Any bone with deformable children/descendants will be output as well.",
3930 default=CONFIG[
'ONLY_DEFORMABLE_BONES'])
3931 EX_ONLY_KEYFRAMED_BONES = BoolProperty(
3932 name=
"Only Keyframed Bones",
3933 description=
"only exports bones that have been keyframed for a given animation. Useful to limit the set of bones on a per-animation basis.",
3934 default=CONFIG[
'ONLY_KEYFRAMED_BONES'])
3935 EX_OGRE_INHERIT_SCALE = BoolProperty(
3936 name=
"OGRE inherit scale",
3937 description=
"whether the OGRE bones have the 'inherit scale' flag on. If the animation has scale in it, the exported animation needs to be adjusted to account for the state of the inherit-scale flag in OGRE.",
3938 default=CONFIG[
'OGRE_INHERIT_SCALE'])
3939 EX_SCENE = BoolProperty(
3940 name=
"Export Scene",
3941 description=
"export current scene (OgreDotScene xml)",
3942 default=CONFIG[
'SCENE'])
3943 EX_SELONLY = BoolProperty(
3944 name=
"Export Selected Only",
3945 description=
"export selected",
3946 default=CONFIG[
'SELONLY'])
3947 EX_EXPORT_HIDDEN = BoolProperty(
3948 name=
"Export Hidden Also",
3949 description=
"Export hidden meshes in addition to visible ones. Turn off to avoid exporting hidden stuff.",
3950 default=CONFIG[
'EXPORT_HIDDEN'])
3951 EX_FORCE_CAMERA = BoolProperty(
3952 name=
"Force Camera",
3953 description=
"export active camera",
3954 default=CONFIG[
'FORCE_CAMERA'])
3955 EX_FORCE_LAMPS = BoolProperty(
3957 description=
"export all lamps",
3958 default=CONFIG[
'FORCE_LAMPS'])
3959 EX_MESH = BoolProperty(
3960 name=
"Export Meshes",
3961 description=
"export meshes",
3962 default=CONFIG[
'MESH'])
3963 EX_MESH_OVERWRITE = BoolProperty(
3964 name=
"Export Meshes (overwrite)",
3965 description=
"export meshes (overwrite existing files)",
3966 default=CONFIG[
'MESH_OVERWRITE'])
3967 EX_ARM_ANIM = BoolProperty(
3968 name=
"Armature Animation",
3969 description=
"export armature animations - updates the .skeleton file",
3970 default=CONFIG[
'ARM_ANIM'])
3971 EX_SHAPE_ANIM = BoolProperty(
3972 name=
"Shape Animation",
3973 description=
"export shape animations - updates the .mesh file",
3974 default=CONFIG[
'SHAPE_ANIM'])
3975 EX_TRIM_BONE_WEIGHTS = FloatProperty(
3976 name=
"Trim Weights",
3977 description=
"ignore bone weights below this value (Ogre supports 4 bones per vertex)",
3978 min=0.0, max=0.5, default=CONFIG[
'TRIM_BONE_WEIGHTS'] )
3979 EX_ARRAY = BoolProperty(
3980 name=
"Optimize Arrays",
3981 description=
"optimize array modifiers as instances (constant offset only)",
3982 default=CONFIG[
'ARRAY'])
3983 EX_MATERIALS = BoolProperty(
3984 name=
"Export Materials",
3985 description=
"exports .material script",
3986 default=CONFIG[
'MATERIALS'])
3987 EX_FORCE_IMAGE_FORMAT = EnumProperty(
3988 items=_IMAGE_FORMATS,
3989 name=
'Convert Images',
3990 description=
'convert all textures to format',
3991 default=CONFIG[
'FORCE_IMAGE_FORMAT'] )
3992 EX_DDS_MIPS = IntProperty(
3994 description=
"number of mip maps (DDS)",
3996 default=CONFIG[
'DDS_MIPS'])
3999 EX_lodLevels = IntProperty(
4001 description=
"MESH number of LOD levels",
4003 default=CONFIG[
'lodLevels'])
4004 EX_lodDistance = IntProperty(
4005 name=
"LOD Distance",
4006 description=
"MESH distance increment to reduce LOD",
4008 default=CONFIG[
'lodDistance'])
4009 EX_lodPercent = IntProperty(
4010 name=
"LOD Percentage",
4011 description=
"LOD percentage reduction",
4013 default=CONFIG[
'lodPercent'])
4014 EX_nuextremityPoints = IntProperty(
4015 name=
"Extremity Points",
4016 description=
"MESH Extremity Points",
4018 default=CONFIG[
'nuextremityPoints'])
4019 EX_generateEdgeLists = BoolProperty(
4021 description=
"MESH generate edge lists (for stencil shadows)",
4022 default=CONFIG[
'generateEdgeLists'])
4023 EX_generateTangents = BoolProperty(
4025 description=
"MESH generate tangents",
4026 default=CONFIG[
'generateTangents'])
4027 EX_tangentSemantic = StringProperty(
4028 name=
"Tangent Semantic",
4029 description=
"MESH tangent semantic",
4031 default=CONFIG[
'tangentSemantic'])
4032 EX_tangentUseParity = IntProperty(
4033 name=
"Tangent Parity",
4034 description=
"MESH tangent use parity",
4036 default=CONFIG[
'tangentUseParity'])
4037 EX_tangentSplitMirrored = BoolProperty(
4038 name=
"Tangent Split Mirrored",
4039 description=
"MESH split mirrored tangents",
4040 default=CONFIG[
'tangentSplitMirrored'])
4041 EX_tangentSplitRotated = BoolProperty(
4042 name=
"Tangent Split Rotated",
4043 description=
"MESH split rotated tangents",
4044 default=CONFIG[
'tangentSplitRotated'])
4045 EX_reorganiseBuffers = BoolProperty(
4046 name=
"Reorganise Buffers",
4047 description=
"MESH reorganise vertex buffers",
4048 default=CONFIG[
'reorganiseBuffers'])
4049 EX_optimiseAnimations = BoolProperty(
4050 name=
"Optimize Animations",
4051 description=
"MESH optimize animations",
4052 default=CONFIG[
'optimiseAnimations'])
4054 filepath= StringProperty(
4056 description=
"Filepath used for exporting Ogre .scene file",
4059 subtype=
'FILE_PATH')
4061 EXPORT_TYPE =
'OGRE' 4065 class INFO_OT_createRealxtendExport( bpy.types.Operator, _OgreCommonExport_):
4066 '''Export RealXtend Scene''' 4067 bl_idname =
"ogre.export_realxtend" 4068 bl_label =
"Export RealXtend" 4069 bl_options = {
'REGISTER',
'UNDO'}
4071 EX_SWAP_AXIS = EnumProperty(
4074 description=
'axis swapping mode',
4075 default= CONFIG[
'SWAP_AXIS']
4079 EX_SEP_MATS = BoolProperty(
4080 name=
"Separate Materials",
4081 description=
"exports a .material for each material (rather than putting all materials in a single .material file)",
4082 default=CONFIG[
'SEP_MATS'])
4083 EX_ONLY_DEFORMABLE_BONES = BoolProperty(
4084 name=
"Only Deformable Bones",
4085 description=
"only exports bones that are deformable. Useful for hiding IK-Bones used in Blender. Note: Any bone with deformable children/descendants will be output as well.",
4086 default=CONFIG[
'ONLY_DEFORMABLE_BONES'])
4087 EX_ONLY_KEYFRAMED_BONES = BoolProperty(
4088 name=
"Only Keyframed Bones",
4089 description=
"only exports bones that have been keyframed for a given animation. Useful to limit the set of bones on a per-animation basis.",
4090 default=CONFIG[
'ONLY_KEYFRAMED_BONES'])
4091 EX_OGRE_INHERIT_SCALE = BoolProperty(
4092 name=
"OGRE inherit scale",
4093 description=
"whether the OGRE bones have the 'inherit scale' flag on. If the animation has scale in it, the exported animation needs to be adjusted to account for the state of the inherit-scale flag in OGRE.",
4094 default=CONFIG[
'OGRE_INHERIT_SCALE'])
4095 EX_SCENE = BoolProperty(
4096 name=
"Export Scene",
4097 description=
"export current scene (OgreDotScene xml)",
4098 default=CONFIG[
'SCENE'])
4099 EX_SELONLY = BoolProperty(
4100 name=
"Export Selected Only",
4101 description=
"export selected",
4102 default=CONFIG[
'SELONLY'])
4103 EX_EXPORT_HIDDEN = BoolProperty(
4104 name=
"Export Hidden Also",
4105 description=
"Export hidden meshes in addition to visible ones. Turn off to avoid exporting hidden stuff.",
4106 default=CONFIG[
'EXPORT_HIDDEN'])
4107 EX_FORCE_CAMERA = BoolProperty(
4108 name=
"Force Camera",
4109 description=
"export active camera",
4110 default=CONFIG[
'FORCE_CAMERA'])
4111 EX_FORCE_LAMPS = BoolProperty(
4113 description=
"export all lamps",
4114 default=CONFIG[
'FORCE_LAMPS'])
4115 EX_MESH = BoolProperty(
4116 name=
"Export Meshes",
4117 description=
"export meshes",
4118 default=CONFIG[
'MESH'])
4119 EX_MESH_OVERWRITE = BoolProperty(
4120 name=
"Export Meshes (overwrite)",
4121 description=
"export meshes (overwrite existing files)",
4122 default=CONFIG[
'MESH_OVERWRITE'])
4123 EX_ARM_ANIM = BoolProperty(
4124 name=
"Armature Animation",
4125 description=
"export armature animations - updates the .skeleton file",
4126 default=CONFIG[
'ARM_ANIM'])
4127 EX_SHAPE_ANIM = BoolProperty(
4128 name=
"Shape Animation",
4129 description=
"export shape animations - updates the .mesh file",
4130 default=CONFIG[
'SHAPE_ANIM'])
4131 EX_TRIM_BONE_WEIGHTS = FloatProperty(
4132 name=
"Trim Weights",
4133 description=
"ignore bone weights below this value (Ogre supports 4 bones per vertex)",
4134 min=0.0, max=0.5, default=CONFIG[
'TRIM_BONE_WEIGHTS'] )
4135 EX_ARRAY = BoolProperty(
4136 name=
"Optimize Arrays",
4137 description=
"optimize array modifiers as instances (constant offset only)",
4138 default=CONFIG[
'ARRAY'])
4139 EX_MATERIALS = BoolProperty(
4140 name=
"Export Materials",
4141 description=
"exports .material script",
4142 default=CONFIG[
'MATERIALS'])
4143 EX_FORCE_IMAGE_FORMAT = EnumProperty(
4144 name=
'Convert Images',
4145 description=
'convert all textures to format',
4146 items=_IMAGE_FORMATS,
4147 default=CONFIG[
'FORCE_IMAGE_FORMAT'])
4148 EX_DDS_MIPS = IntProperty(
4150 description=
"number of mip maps (DDS)",
4152 default=CONFIG[
'DDS_MIPS'])
4155 EX_lodLevels = IntProperty(
4157 description=
"MESH number of LOD levels",
4159 default=CONFIG[
'lodLevels'])
4160 EX_lodDistance = IntProperty(
4161 name=
"LOD Distance",
4162 description=
"MESH distance increment to reduce LOD",
4164 default=CONFIG[
'lodDistance'])
4165 EX_lodPercent = IntProperty(
4166 name=
"LOD Percentage",
4167 description=
"LOD percentage reduction",
4169 default=CONFIG[
'lodPercent'])
4170 EX_nuextremityPoints = IntProperty(
4171 name=
"Extremity Points",
4172 description=
"MESH Extremity Points",
4174 default=CONFIG[
'nuextremityPoints'])
4175 EX_generateEdgeLists = BoolProperty(
4177 description=
"MESH generate edge lists (for stencil shadows)",
4178 default=CONFIG[
'generateEdgeLists'])
4179 EX_generateTangents = BoolProperty(
4181 description=
"MESH generate tangents",
4182 default=CONFIG[
'generateTangents'])
4183 EX_tangentSemantic = StringProperty(
4184 name=
"Tangent Semantic",
4185 description=
"MESH tangent semantic",
4187 default=CONFIG[
'tangentSemantic'])
4188 EX_tangentUseParity = IntProperty(
4189 name=
"Tangent Parity",
4190 description=
"MESH tangent use parity",
4192 default=CONFIG[
'tangentUseParity'])
4193 EX_tangentSplitMirrored = BoolProperty(
4194 name=
"Tangent Split Mirrored",
4195 description=
"MESH split mirrored tangents",
4196 default=CONFIG[
'tangentSplitMirrored'])
4197 EX_tangentSplitRotated = BoolProperty(
4198 name=
"Tangent Split Rotated",
4199 description=
"MESH split rotated tangents",
4200 default=CONFIG[
'tangentSplitRotated'])
4201 EX_reorganiseBuffers = BoolProperty(
4202 name=
"Reorganise Buffers",
4203 description=
"MESH reorganise vertex buffers",
4204 default=CONFIG[
'reorganiseBuffers'])
4205 EX_optimiseAnimations = BoolProperty(
4206 name=
"Optimize Animations",
4207 description=
"MESH optimize animations",
4208 default=CONFIG[
'optimiseAnimations'])
4210 filepath = StringProperty(
4212 description=
"Filepath used for exporting .txml file",
4215 subtype=
'FILE_PATH')
4221 def _ogre_node_helper( doc, ob, objects, prefix='', pos=None, rot=None, scl=None ):
4223 mat = get_parent_matrix(ob, objects).inverted() * ob.matrix_world
4225 o = doc.createElement(
'node')
4226 o.setAttribute(
'name',prefix+ob.name)
4227 p = doc.createElement(
'position')
4228 q = doc.createElement(
'rotation')
4229 s = doc.createElement(
'scale')
4236 v =
swap( mat.to_translation() )
4237 p.setAttribute(
'x',
'%6f'%v.x)
4238 p.setAttribute(
'y',
'%6f'%v.y)
4239 p.setAttribute(
'z',
'%6f'%v.z)
4244 v =
swap( mat.to_quaternion() )
4245 q.setAttribute(
'qx',
'%6f'%v.x)
4246 q.setAttribute(
'qy',
'%6f'%v.y)
4247 q.setAttribute(
'qz',
'%6f'%v.z)
4248 q.setAttribute(
'qw',
'%6f'%v.w)
4252 x=abs(v.x); y=abs(v.y); z=abs(v.z)
4253 s.setAttribute(
'x',
'%6f'%x)
4254 s.setAttribute(
'y',
'%6f'%y)
4255 s.setAttribute(
'z',
'%6f'%z)
4257 ri = mat.to_quaternion().inverted().to_matrix()
4258 scale = ri.to_4x4() * mat
4259 v =
swap( scale.to_scale() )
4260 x=abs(v.x); y=abs(v.y); z=abs(v.z)
4261 s.setAttribute(
'x',
'%6f'%x)
4262 s.setAttribute(
'y',
'%6f'%y)
4263 s.setAttribute(
'z',
'%6f'%z)
4268 class MeshMagick(object):
4269 ''' Usage: MeshMagick [global_options] toolname [tool_options] infile(s) -- [outfile(s)] 4272 info - print information about the mesh. 4273 meshmerge - Merge multiple submeshes into a single mesh. 4274 optimise - Optimise meshes and skeletons. 4275 rename - Rename different elements of meshes and skeletons. 4276 transform - Scale, rotate or otherwise transform a mesh. 4280 def get_merge_group( ob ):
4281 return get_merge_group( ob, prefix=
'magicmerge' )
4284 def merge( group, path='/tmp', force_name=None ):
4286 print(
' mesh magick - merge ')
4287 exe = CONFIG[
'OGRETOOLS_MESH_MAGICK']
4288 if not os.path.isfile( exe ):
4289 print(
'ERROR: can not find MeshMagick.exe' )
4294 for ob
in group.objects:
4295 if ob.data.users == 1:
4296 files.append( os.path.join( path, ob.data.name+
'.mesh' ) )
4300 if sys.platform ==
'linux2': cmd =
'/usr/bin/wine %s %s' %(exe, opts)
4301 else: cmd =
'%s %s' %(exe, opts)
4302 if force_name: output = force_name +
'.mesh' 4303 else: output =
'_%s_.mesh' %group.name
4304 cmd = cmd.split() + files + [
'--', os.path.join(path,output) ]
4305 subprocess.call( cmd )
4306 print(
' mesh magick - complete ')
4311 _ogre_command_line_tools_doc =
''' 4312 Usage: OgreXMLConverter [options] sourcefile [destfile] 4315 -i = interactive mode - prompt for options 4316 (The next 4 options are only applicable when converting XML to Mesh) 4317 -l lodlevels = number of LOD levels 4318 -v lodvalue = value increment to reduce LOD 4319 -s lodstrategy = LOD strategy to use for this mesh 4320 -p lodpercent = Percentage triangle reduction amount per LOD 4321 -f lodnumtris = Fixed vertex reduction per LOD 4322 -e = DON'T generate edge lists (for stencil shadows) 4323 -r = DON'T reorganise vertex buffers to OGRE recommended format. 4324 -t = Generate tangents (for normal mapping) 4326 = Tangent vertex semantic destination (default tangent) 4327 -ts [3|4] = Tangent size (3 or 4 components, 4 includes parity, default 3) 4328 -tm = Split tangent vertices at UV mirror points 4329 -tr = Split tangent vertices where basis is rotated > 90 degrees 4330 -o = DON'T optimise out redundant tracks & keyframes 4331 -d3d = Prefer D3D packed colour formats (default on Windows) 4332 -gl = Prefer GL packed colour formats (default on non-Windows) 4333 -E endian = Set endian mode 'big' 'little' or 'native' (default) 4334 -x num = Generate no more than num eXtremes for every submesh (default 0) 4335 -q = Quiet mode, less output 4336 -log filename = name of the log file (default: 'OgreXMLConverter.log') 4337 sourcefile = name of file to convert 4338 destfile = optional name of file to write to. If you don't 4339 specify this OGRE works it out through the extension 4340 and the XML contents if the source is XML. For example 4341 test.mesh becomes test.xml, test.xml becomes test.mesh 4342 if the XML document root is <mesh> etc. 4347 def OgreXMLConverter( infile, has_uvs=False ):
4350 exe = CONFIG[
'OGRETOOLS_XML_CONVERTER']
4351 if not os.path.isfile( exe ):
4352 print(
'WARNING: can not find OgreXMLConverter (can not convert XXX.mesh.xml to XXX.mesh' )
4362 if CONFIG[
'nuextremityPoints'] > 0:
4363 basicArguments +=
' -x %s' %CONFIG[
'nuextremityPoints']
4365 if not CONFIG[
'generateEdgeLists']:
4366 basicArguments +=
' -e' 4369 if CONFIG[
'generateTangents']
and has_uvs:
4370 basicArguments +=
' -t' 4371 if CONFIG[
'tangentSemantic']:
4372 basicArguments +=
' -td %s' %CONFIG[
'tangentSemantic']
4373 if CONFIG[
'tangentUseParity']:
4374 basicArguments +=
' -ts %s' %CONFIG[
'tangentUseParity']
4375 if CONFIG[
'tangentSplitMirrored']:
4376 basicArguments +=
' -tm' 4377 if CONFIG[
'tangentSplitRotated']:
4378 basicArguments +=
' -tr' 4379 if not CONFIG[
'reorganiseBuffers']:
4380 basicArguments +=
' -r' 4381 if not CONFIG[
'optimiseAnimations']:
4382 basicArguments +=
' -o' 4385 basicArguments +=
' -q' 4387 opts =
'-log _ogre_debug.txt %s' %basicArguments
4388 path,name = os.path.split( infile )
4390 cmd =
'%s %s' %(exe, opts)
4391 cmd = cmd.split() + [infile]
4392 subprocess.call( cmd )
4398 def __init__(self, rbone, pbone, skeleton):
4399 if CONFIG[
'SWAP_AXIS'] ==
'xyz':
4400 self.fixUpAxis =
False 4402 self.fixUpAxis =
True 4403 if CONFIG[
'SWAP_AXIS'] ==
'-xzy':
4404 self.flipMat = mathutils.Matrix(((-1,0,0,0),(0,0,1,0),(0,1,0,0),(0,0,0,1)))
4405 elif CONFIG[
'SWAP_AXIS'] ==
'xz-y':
4407 self.flipMat = mathutils.Matrix(((1,0,0,0),(0,0,1,0),(0,-1,0,0),(0,0,0,1)))
4409 print(
'ERROR - TODO: axis swap mode not supported with armature animation' )
4412 self.skeleton = skeleton
4413 self.name = pbone.name
4414 self.matrix = rbone.matrix_local.copy()
4418 self.shouldOutput =
True 4419 if CONFIG[
'ONLY_DEFORMABLE_BONES']
and not pbone.bone.use_deform:
4420 self.shouldOutput =
False 4423 self.parent = pbone.parent
4428 pose = pbone.matrix.copy()
4429 self._inverse_total_trans_pose = pose.inverted()
4432 pose = self.parent._inverse_total_trans_pose* pose
4433 elif self.fixUpAxis:
4434 pose = self.flipMat * pose
4438 self.pose_location = pose.to_translation() - self.ogre_rest_matrix.to_translation()
4439 pose = self.inverse_ogre_rest_matrix * pose
4440 self.pose_rotation = pose.to_quaternion()
4449 if CONFIG[
'OGRE_INHERIT_SCALE']:
4456 self.pose_scale = pose.to_scale()
4457 self.ogreDerivedScale = self.pose_scale.copy()
4460 self.ogreDerivedScale[0] *= self.parent.ogreDerivedScale[0]
4461 self.ogreDerivedScale[1] *= self.parent.ogreDerivedScale[1]
4462 self.ogreDerivedScale[2] *= self.parent.ogreDerivedScale[2]
4464 if not self.bone.bone.use_inherit_scale:
4466 scl = self.parent.ogreDerivedScale
4467 self.pose_scale = mathutils.Vector((1.0/scl[0], 1.0/scl[1], 1.0/scl[2]))
4468 self.ogreDerivedScale = mathutils.Vector((1.0, 1.0, 1.0))
4472 self.pose_scale = pbone.scale.copy()
4474 if self.parent
and self.bone.bone.use_inherit_scale:
4476 self.pose_scale[0] *= self.parent.pose_scale[0]
4477 self.pose_scale[1] *= self.parent.pose_scale[1]
4478 self.pose_scale[2] *= self.parent.pose_scale[2]
4480 for child
in self.children:
4483 def clear_pose_transform( self ):
4484 self.bone.location.zero()
4485 self.bone.scale.Fill(3, 1.0)
4486 self.bone.rotation_quaternion.identity()
4487 self.bone.rotation_euler.zero()
4490 def save_pose_transform( self ):
4491 self.savedPoseLocation = self.bone.location.copy()
4492 self.savedPoseScale = self.bone.scale.copy()
4493 self.savedPoseRotationQ = self.bone.rotation_quaternion
4494 self.savedPoseRotationE = self.bone.rotation_euler
4497 def restore_pose_transform( self ):
4498 self.bone.location = self.savedPoseLocation
4499 self.bone.scale = self.savedPoseScale
4500 self.bone.rotation_quaternion = self.savedPoseRotationQ
4501 self.bone.rotation_euler = self.savedPoseRotationE
4504 def rebuild_tree( self ):
4506 self.parent = self.skeleton.get_bone( self.parent.name )
4507 self.parent.children.append( self )
4508 if self.shouldOutput
and not self.parent.shouldOutput:
4510 parent = self.parent
4512 parent.shouldOutput =
True 4513 parent = parent.parent
4515 def compute_rest( self ):
4517 inverseParentMatrix = self.parent.inverse_total_trans
4518 elif self.fixUpAxis:
4519 inverseParentMatrix = self.flipMat
4521 inverseParentMatrix = mathutils.Matrix(((1,0,0,0),(0,1,0,0),(0,0,1,0),(0,0,0,1)))
4524 self.ogre_rest_matrix = self.matrix.copy()
4526 self.inverse_total_trans = self.ogre_rest_matrix.inverted()
4528 self.ogre_rest_matrix = inverseParentMatrix * self.ogre_rest_matrix
4529 self.inverse_ogre_rest_matrix = self.ogre_rest_matrix.inverted()
4532 for child
in self.children:
4533 child.compute_rest()
4536 def __init__(self, time, pos, rot, scale):
4538 self.pos = pos.copy()
4539 self.rot = rot.copy()
4540 self.scale = scale.copy()
4542 def isTransIdentity( self ):
4543 return self.pos.length < 0.0001
4545 def isRotIdentity( self ):
4547 if abs(self.rot.angle) < 0.0001
or abs(self.rot.axis.length - 1.0) > 0.001:
4552 def isScaleIdentity( self ):
4553 scaleDiff = mathutils.Vector((1,1,1)) - self.scale
4554 return scaleDiff.length < 0.0001
4561 def __init__(self, bone):
4565 def is_pos_animated( self ):
4567 for kf
in self.keyframes:
4568 if not kf.isTransIdentity():
4572 def is_rot_animated( self ):
4574 for kf
in self.keyframes:
4575 if not kf.isRotIdentity():
4579 def is_scale_animated( self ):
4581 for kf
in self.keyframes:
4582 if not kf.isScaleIdentity():
4586 def add_keyframe( self, time ):
4588 kf = Keyframe(time, bone.pose_location, bone.pose_rotation, bone.pose_scale)
4589 self.keyframes.append( kf )
4591 def write_track( self, doc, tracks_element ):
4592 isPosAnimated = self.is_pos_animated()
4593 isRotAnimated = self.is_rot_animated()
4594 isScaleAnimated = self.is_scale_animated()
4595 if not isPosAnimated
and not isRotAnimated
and not isScaleAnimated:
4597 track = doc.createElement(
'track')
4598 track.setAttribute(
'bone', self.bone.name)
4599 keyframes_element = doc.createElement(
'keyframes')
4600 track.appendChild( keyframes_element )
4601 for kf
in self.keyframes:
4602 keyframe = doc.createElement(
'keyframe')
4603 keyframe.setAttribute(
'time',
'%6f' % kf.time)
4605 trans = doc.createElement(
'translate')
4606 keyframe.appendChild( trans )
4607 trans.setAttribute(
'x',
'%6f' % kf.pos.x)
4608 trans.setAttribute(
'y',
'%6f' % kf.pos.y)
4609 trans.setAttribute(
'z',
'%6f' % kf.pos.z)
4612 rotElement = doc.createElement(
'rotate' )
4613 keyframe.appendChild( rotElement )
4614 angle = kf.rot.angle
4617 if kf.isRotIdentity():
4619 axis = mathutils.Vector((0,0,0))
4620 rotElement.setAttribute(
'angle',
'%6f' %angle )
4621 axisElement = doc.createElement(
'axis')
4622 rotElement.appendChild( axisElement )
4623 axisElement.setAttribute(
'x',
'%6f' %axis[0])
4624 axisElement.setAttribute(
'y',
'%6f' %axis[1])
4625 axisElement.setAttribute(
'z',
'%6f' %axis[2])
4628 scale = doc.createElement(
'scale')
4629 keyframe.appendChild( scale )
4631 scale.setAttribute(
'x',
'%6f' %x)
4632 scale.setAttribute(
'y',
'%6f' %y)
4633 scale.setAttribute(
'z',
'%6f' %z)
4634 keyframes_element.appendChild( keyframe )
4635 tracks_element.appendChild( track )
4638 def findArmature( ob ):
4639 arm = ob.find_armature()
4641 if not arm.animation_data:
4643 for ob2
in bpy.data.objects:
4644 if ob2.type ==
'ARMATURE' and ob2.proxy == arm:
4645 print(
"proxy armature %s found" % ob2.name )
4649 class Skeleton(object):
4650 def get_bone( self, name ):
4651 for b
in self.bones:
4656 def __init__(self, ob ):
4657 if ob.location.x != 0
or ob.location.y != 0
or ob.location.z != 0:
4658 Report.warnings.append(
'ERROR: Mesh (%s): is offset from Armature - zero transform is required' %ob.name)
4659 if ob.scale.x != 1
or ob.scale.y != 1
or ob.scale.z != 1:
4660 Report.warnings.append(
'ERROR: Mesh (%s): has been scaled - scale(1,1,1) is required' %ob.name)
4665 self.arm = arm = findArmature( ob )
4667 self._restore_layers = list(arm.layers)
4670 for pbone
in arm.pose.bones:
4671 mybone = Bone( arm.data.bones[pbone.name], pbone, self )
4672 self.bones.append( mybone )
4674 if arm.name
not in Report.armatures:
4675 Report.armatures.append( arm.name )
4682 x,y,z = arm.matrix_local.to_euler()
4683 if x != 0
or y != 0
or z != 0:
4684 Report.warnings.append(
'ERROR: Armature: %s is rotated - (rotation is ignored)' %arm.name)
4687 for b
in self.bones:
4692 for b
in self.bones:
4695 loc,rot,scl = b.ogre_rest_matrix.decompose()
4700 self.roots.append( b )
4702 def write_animation( self, arm, actionName, frameBegin, frameEnd, doc, parentElement ):
4703 _fps = float( bpy.context.scene.render.fps )
4706 for bone
in self.bones:
4708 if bone.shouldOutput:
4709 bone_tracks.append( Bone_Track(bone) )
4710 bone.clear_pose_transform()
4711 for frame
in range( int(frameBegin), int(frameEnd)+1, bpy.context.scene.frame_step):
4712 bpy.context.scene.frame_set(frame)
4713 for bone
in self.roots:
4715 for track
in bone_tracks:
4716 track.add_keyframe((frame - frameBegin) / _fps)
4718 animationFound =
False 4719 for track
in bone_tracks:
4720 if track.is_pos_animated()
or track.is_rot_animated()
or track.is_scale_animated():
4721 animationFound =
True 4723 if not animationFound:
4725 anim = doc.createElement(
'animation')
4726 parentElement.appendChild( anim )
4727 tracks = doc.createElement(
'tracks')
4728 anim.appendChild( tracks )
4729 Report.armature_animations.append(
'%s : %s [start frame=%s end frame=%s]' %(arm.name, actionName, frameBegin, frameEnd) )
4731 anim.setAttribute(
'name', actionName)
4732 anim.setAttribute(
'length',
'%6f' %( (frameEnd - frameBegin)/_fps ) )
4734 for track
in bone_tracks:
4736 track.write_track( doc, tracks )
4740 root = doc.createElement(
'skeleton'); doc.appendChild( root )
4741 bones = doc.createElement(
'bones'); root.appendChild( bones )
4742 bh = doc.createElement(
'bonehierarchy'); root.appendChild( bh )
4744 for bone
in self.bones:
4745 if not bone.shouldOutput:
4747 b = doc.createElement(
'bone')
4748 b.setAttribute(
'name', bone.name)
4749 b.setAttribute(
'id', str(boneId) )
4751 bones.appendChild( b )
4752 mat = bone.ogre_rest_matrix.copy()
4754 bp = doc.createElement(
'boneparent')
4755 bp.setAttribute(
'bone', bone.name)
4756 bp.setAttribute(
'parent', bone.parent.name)
4757 bh.appendChild( bp )
4759 pos = doc.createElement(
'position' ); b.appendChild( pos )
4760 x,y,z = mat.to_translation()
4761 pos.setAttribute(
'x',
'%6f' %x )
4762 pos.setAttribute(
'y',
'%6f' %y )
4763 pos.setAttribute(
'z',
'%6f' %z )
4764 rot = doc.createElement(
'rotation' )
4765 b.appendChild( rot )
4767 q = mat.to_quaternion()
4768 rot.setAttribute(
'angle',
'%6f' %q.angle )
4769 axis = doc.createElement(
'axis'); rot.appendChild( axis )
4771 axis.setAttribute(
'x',
'%6f' %x )
4772 axis.setAttribute(
'y',
'%6f' %y )
4773 axis.setAttribute(
'z',
'%6f' %z )
4779 savedFrame = bpy.context.scene.frame_current
4781 for b
in self.bones:
4782 b.save_pose_transform()
4784 anims = doc.createElement(
'animations')
4785 root.appendChild( anims )
4786 if not arm.animation_data
or (arm.animation_data
and not arm.animation_data.nla_tracks):
4788 self.write_animation( arm,
'my_animation', bpy.context.scene.frame_start, bpy.context.scene.frame_end, doc, anims )
4790 elif arm.animation_data:
4791 savedUseNla = arm.animation_data.use_nla
4792 savedAction = arm.animation_data.action
4793 arm.animation_data.use_nla =
False 4794 if not len( arm.animation_data.nla_tracks ):
4795 Report.warnings.append(
'you must assign an NLA strip to armature (%s) that defines the start and end frames' %arm.name)
4800 for nla
in arm.animation_data.nla_tracks:
4801 print(
'NLA track:', nla.name)
4803 for strip
in nla.strips:
4804 action = strip.action
4805 actions[ action.name ] = action
4806 print(
' strip name:', strip.name)
4807 print(
' action name:', action.name)
4809 actionNames = sorted( actions.keys() )
4810 for actionName
in actionNames:
4811 action = actions[ actionName ]
4812 arm.animation_data.action = action
4813 suppressedBones = []
4814 if CONFIG[
'ONLY_KEYFRAMED_BONES']:
4816 for group
in action.groups:
4817 keyframedBones[ group.name ] =
True 4818 for b
in self.bones:
4819 if (
not b.name
in keyframedBones)
and b.shouldOutput:
4821 b.shouldOutput =
False 4822 suppressedBones.append( b.name )
4823 self.write_animation( arm, actionName, action.frame_range[0], action.frame_range[1], doc, anims )
4825 for boneName
in suppressedBones:
4826 bone = self.get_bone( boneName )
4827 bone.shouldOutput =
True 4829 arm.animation_data.action = savedAction
4830 arm.animation_data.use_nla = savedUseNla
4833 bpy.context.scene.frame_set( savedFrame )
4835 for b
in self.bones:
4836 b.restore_pose_transform()
4838 return doc.toprettyxml()
4843 class INFO_MT_instances(bpy.types.Menu):
4844 bl_label =
"Instances" 4846 def draw(self, context):
4847 layout = self.layout
4848 inst = gather_instances()
4851 op = layout.operator(INFO_MT_instance.bl_idname, text=ob.name)
4852 op.mystring = ob.name
4855 class INFO_MT_instance(bpy.types.Operator):
4856 '''select instance group''' 4857 bl_idname =
"ogre.select_instances" 4858 bl_label =
"Select Instance Group" 4859 bl_options = {
'REGISTER',
'UNDO'}
4860 mystring= StringProperty(name=
"MyString", description=
"hidden string", maxlen=1024, default=
"my string")
4863 def poll(cls, context):
4866 def invoke(self, context, event):
4867 print(
'invoke select_instances op', event )
4868 select_instances( context, self.mystring )
4871 class INFO_MT_groups(bpy.types.Menu):
4874 def draw(self, context):
4875 layout = self.layout
4876 for group
in bpy.data.groups:
4877 op = layout.operator(INFO_MT_group.bl_idname, text=group.name)
4878 op.mystring = group.name
4881 class INFO_MT_group(bpy.types.Operator):
4883 bl_idname =
"ogre.select_group" 4884 bl_label =
"Select Group" 4885 bl_options = {
'REGISTER'}
4886 mystring= StringProperty(name=
"MyString", description=
"hidden string", maxlen=1024, default=
"my string")
4889 def poll(cls, context):
4892 def invoke(self, context, event):
4893 select_group( context, self.mystring )
4903 creates normal maps from color or alpha 4906 writes out .dds file 4907 does batch processing 4908 reads .tga, .bmp, .gif, .ppm, .jpg, .tif, .cel, .dds, .png, .psd, .rgb, *.bw and .rgba 4912 -profile <profile name> : Read a profile created from the Photoshop plugin 4913 -quick : use fast compression method 4914 -quality_normal : normal quality compression 4915 -quality_production : production quality compression 4916 -quality_highest : highest quality compression (this can be very slow) 4917 -rms_threshold <int> : quality RMS error. Above this, an extensive search is performed. 4918 -prescale <int> <int>: rescale image to this size first 4919 -rescale <nearest | hi | lo | next_lo>: rescale image to nearest, next highest or next lowest power of two 4920 -rel_scale <float, float> : relative scale of original image. 0.5 is half size Default 1.0, 1.0 4922 Optional Filtering for rescaling. Default cube filter: 4937 -clamp <int, int> : maximum image size. image width and height are clamped 4938 -clampScale <int, int> : maximum image size. image width and height are scaled 4939 -window <left, top, right, bottom> : window of original window to compress 4940 -nomipmap : don't generate MIP maps 4941 -nmips <int> : specify the number of MIP maps to generate 4942 -rgbe : Image is RGBE format 4943 -dither : add dithering 4944 -sharpenMethod <method>: sharpen method MIP maps 4962 UnSharp <radius, amount, threshold> 4963 XSharpen <xsharpen_strength, xsharpen_threshold> 4965 -pause : wait for keyboard on error 4966 -flip : flip top to bottom 4967 -timestamp : Update only changed files 4968 -list <filename> : list of files to convert 4969 -cubeMap : create cube map . 4970 Cube faces specified with individual files with -list option 4971 positive x, negative x, positive y, negative y, positive z, negative z 4972 Use -output option to specify filename 4973 Cube faces specified in one file. Use -file to specify input filename 4975 -volumeMap : create volume texture. 4976 Volume slices specified with individual files with -list option 4977 Use -output option to specify filename 4978 Volume specified in one file. Use -file to specify input filename 4980 -all : all image files in current directory 4981 -outdir <directory>: output directory 4982 -deep [directory]: include all subdirectories 4983 -outsamedir : output directory same as input 4984 -overwrite : if input is .dds file, overwrite old file 4985 -forcewrite : write over readonly files 4986 -file <filename> : input file to process. Accepts wild cards 4987 -output <filename> : filename to write to [-outfile can also be specified] 4988 -append <filename_append> : append this string to output filename 4989 -8 <dxt1c | dxt1a | dxt3 | dxt5 | u1555 | u4444 | u565 | u8888 | u888 | u555 | L8 | A8> : compress 8 bit images with this format 4990 -16 <dxt1c | dxt1a | dxt3 | dxt5 | u1555 | u4444 | u565 | u8888 | u888 | u555 | A8L8> : compress 16 bit images with this format 4991 -24 <dxt1c | dxt1a | dxt3 | dxt5 | u1555 | u4444 | u565 | u8888 | u888 | u555> : compress 24 bit images with this format 4992 -32 <dxt1c | dxt1a | dxt3 | dxt5 | u1555 | u4444 | u565 | u8888 | u888 | u555> : compress 32 bit images with this format 4996 -gamma <float value>: gamma correcting during filtering 4997 -outputScale <float, float, float, float>: scale the output by this (r,g,b,a) 4998 -outputBias <float, float, float, float>: bias the output by this amount (r,g,b,a) 4999 -outputWrap : wraps overflow values modulo the output format 5000 -inputScale <float, float, float, float>: scale the inpput by this (r,g,b,a) 5001 -inputBias <float, float, float, float>: bias the input by this amount (r,g,b,a) 5002 -binaryalpha : treat alpha as 0 or 1 5003 -alpha_threshold <byte>: [0-255] alpha reference value 5004 -alphaborder : border images with alpha = 0 5005 -alphaborderLeft : border images with alpha (left) = 0 5006 -alphaborderRight : border images with alpha (right)= 0 5007 -alphaborderTop : border images with alpha (top) = 0 5008 -alphaborderBottom : border images with alpha (bottom)= 0 5009 -fadeamount <int>: percentage to fade each MIP level. Default 15 5011 -fadecolor : fade map (color, normal or DuDv) over MIP levels 5012 -fadetocolor <hex color> : color to fade to 5013 -custom_fade <n> <n fadeamounts> : set custom fade amount. n is number number of fade amounts. fadeamount are [0,1] 5014 -fadealpha : fade alpha over MIP levels 5015 -fadetoalpha <byte>: [0-255] alpha to fade to 5016 -border : border images with color 5017 -bordercolor <hex color> : color for border 5018 -force4 : force DXT1c to use always four colors 5019 -weight <float, float, float>: Compression weightings for R G and B 5020 -luminance : convert color values to luminance for L8 formats 5021 -greyScale : Convert to grey scale 5022 -greyScaleWeights <float, float, float, float>: override greyscale conversion weights of (0.3086, 0.6094, 0.0820, 0) 5023 -brightness <float, float, float, float>: per channel brightness. Default 0.0 usual range [0,1] 5024 -contrast <float, float, float, float>: per channel contrast. Default 1.0 usual range [0.5, 1.5] 5026 Texture Format Default DXT3: 5027 -dxt1c : DXT1 (color only) 5028 -dxt1a : DXT1 (one bit alpha) 5031 -u1555 : uncompressed 1:5:5:5 5032 -u4444 : uncompressed 4:4:4:4 5033 -u565 : uncompressed 5:6:5 5034 -u8888 : uncompressed 8:8:8:8 5035 -u888 : uncompressed 0:8:8:8 5036 -u555 : uncompressed 0:5:5:5 5037 -p8c : paletted 8 bit (256 colors) 5038 -p8a : paletted 8 bit (256 colors with alpha) 5039 -p4c : paletted 4 bit (16 colors) 5040 -p4a : paletted 4 bit (16 colors with alpha) 5041 -a8 : 8 bit alpha channel 5042 -cxv8u8 : normal map format 5043 -v8u8 : EMBM format (8, bit two component signed) 5044 -v16u16 : EMBM format (16 bit, two component signed) 5045 -A8L8 : 8 bit alpha channel, 8 bit luminance 5046 -fp32x4 : fp32 four channels (A32B32G32R32F) 5047 -fp32 : fp32 one channel (R32F) 5048 -fp16x4 : fp16 four channels (A16B16G16R16F) 5049 -dxt5nm : dxt5 style normal map 5051 -g16r16 : 16 bit in, two component 5052 -g16r16f : 16 bit float, two components 5054 Mip Map Filtering Options. Default box filter: 5070 *************************** 5071 To make a normal or dudv map, specify one of 5072 -n4 : normal map 4 sample 5073 -n3x3 : normal map 3x3 filter 5074 -n5x5 : normal map 5x5 filter 5075 -n7x7 : normal map 7x7 filter 5076 -n9x9 : normal map 9x9 filter 5079 and source of height info: 5080 -alpha : alpha channel 5082 -biased : average rgb biased 5084 -green : green channel 5085 -blue : blue channel 5086 -max : max of (r,g,b) 5087 -colorspace : mix of r,g,b 5089 -norm : normalize mip maps (source is a normal map) 5091 -toHeight : create a height map (source is a normal map) 5094 Normal/DuDv Map dxt: 5095 -aheight : store calculated height in alpha field 5096 -aclear : clear alpha channel 5097 -awhite : set alpha channel = 1.0 5098 -scale <float> : scale of height map. Default 1.0 5099 -wrap : wrap texture around. Default off 5100 -minz <int> : minimum value for up vector [0-255]. Default 0 5102 *************************** 5103 To make a depth sprite, specify: 5106 and source of depth info: 5107 -alpha : alpha channel 5108 -rgb : average rgb (default) 5110 -green : green channel 5111 -blue : blue channel 5112 -max : max of (r,g,b) 5113 -colorspace : mix of r,g,b 5116 -aheight : store calculated depth in alpha channel 5117 -aclear : store 0.0 in alpha channel 5118 -awhite : store 1.0 in alpha channel 5119 -scale <float> : scale of depth sprite (default 1.0) 5120 -alpha_modulate : multiplies color by alpha during filtering 5121 -pre_modulate : multiplies color by alpha before processing 5124 nvdxt -cubeMap -list cubemapfile.lst -output cubemap.dds 5125 nvdxt -cubeMap -file cubemapfile.tga 5126 nvdxt -file test.tga -dxt1c 5128 nvdxt -file c:\temp\*.tga 5129 nvdxt -file temp\*.tga 5130 nvdxt -file height_field_in_alpha.tga -n3x3 -alpha -scale 10 -wrap 5131 nvdxt -file grey_scale_height_field.tga -n5x5 -rgb -scale 1.3 5132 nvdxt -file normal_map.tga -norm 5133 nvdxt -file image.tga -dudv -fade -fadeamount 10 5134 nvdxt -all -dxt3 -gamma -outdir .\dds_dir -time 5135 nvdxt -file *.tga -depth -max -scale 0.5 5140 import io_export_rogremesh.rogremesh
as Rmesh
5143 print(
'WARNING: "io_export_rogremesh" is missing' )
5145 if Rmesh
and Rmesh.rpy.load():
5146 _USE_RPYTHON_ =
True 5148 _USE_RPYTHON_ =
False 5149 print(
'Rpython module is not cached, you must exit Blender to compile the module:' )
5150 print(
'cd io_export_rogremesh; python rogremesh.py' )
5152 class VertexNoPos(object):
5153 def __init__(self, ogre_vidx, nx,ny,nz, r,g,b,ra, vert_uvs):
5154 self.ogre_vidx = ogre_vidx
5162 self.vert_uvs = vert_uvs
5164 '''does not compare ogre_vidx (and position at the moment) [ no need to compare position ]''' 5165 def __eq__(self, o):
5166 if self.nx != o.nx
or self.ny != o.ny
or self.nz != o.nz:
return False 5167 elif self.r != o.r
or self.g != o.g
or self.b != o.b
or self.ra != o.ra:
return False 5168 elif len(self.vert_uvs) != len(o.vert_uvs):
return False 5170 for i, uv1
in enumerate( self.vert_uvs ):
5171 uv2 = o.vert_uvs[ i ]
5172 if uv1 != uv2:
return False 5177 def dot_mesh( ob, path='/tmp', force_name=None, ignore_shape_animation=False, normals=True, isLOD=False):
5182 if not os.path.isdir( path ):
5183 print(
'>> Creating working directory', path )
5186 Report.meshes.append( ob.data.name )
5187 Report.faces += len( ob.data.tessfaces )
5188 Report.orig_vertices += len( ob.data.vertices )
5196 for mod
in copy.modifiers:
5197 if mod.type
in 'ARMATURE ARRAY'.split(): rem.append( mod )
5198 for mod
in rem: copy.modifiers.remove( mod )
5200 mesh = copy.to_mesh(bpy.context.scene,
True,
"PREVIEW")
5205 name = force_name
or ob.data.name
5206 name = clean_object_name(name)
5207 xmlfile = os.path.join(path,
'%s.mesh.xml' % name )
5210 print(
' - Generating:',
'%s.mesh.xml' % name)
5212 if _USE_RPYTHON_
and False:
5213 Rmesh.save( ob, xmlfile )
5217 f = open( xmlfile,
'w' )
5218 except Exception
as e:
5219 show_dialog(
"Invalid mesh object name: " + name)
5222 doc = SimpleSaxWriter(f,
'mesh', {})
5225 doc.start_tag(
'sharedgeometry', {
'vertexcount' :
'__TO_BE_REPLACED_VERTEX_COUNT__'})
5228 print(
' - Writing shared geometry')
5230 doc.start_tag(
'vertexbuffer', {
5233 'colours_diffuse' : str(bool( mesh.vertex_colors )),
5234 'texture_coords' :
'%s' % len(mesh.uv_textures)
if mesh.uv_textures.active
else '0' 5239 vcolors_alpha =
None 5240 if len( mesh.tessface_vertex_colors ):
5241 vcolors = mesh.tessface_vertex_colors[0]
5242 for bloc
in mesh.tessface_vertex_colors:
5243 if bloc.name.lower().startswith(
'alpha'):
5244 vcolors_alpha = bloc;
break 5248 for mat
in ob.data.materials:
5250 materials.append( mat )
5252 print(
'[WARNING:] Bad material data in', ob)
5253 materials.append(
'_missing_material_' )
5255 materials.append(
'_missing_material_' )
5257 for matidx, mat
in enumerate( materials ):
5258 _sm_faces_.append([])
5263 if mesh.tessface_uv_textures.active:
5265 for layer
in mesh.tessface_uv_textures:
5266 uvs = []; uvcache.append( uvs )
5267 for uvface
in layer.data:
5268 uvs.append( (uvface.uv1, uvface.uv2, uvface.uv3, uvface.uv4) )
5274 for F
in mesh.tessfaces:
5275 smooth = F.use_smooth
5276 faces = _sm_faces_[ F.material_index ]
5279 tris.append( (F.vertices[0], F.vertices[1], F.vertices[2]) )
5280 if len(F.vertices) >= 4:
5281 tris.append( (F.vertices[0], F.vertices[2], F.vertices[3]) )
5285 for layer
in uvcache:
5286 uv1, uv2, uv3, uv4 = layer[ F.index ]
5287 a.append( (uv1, uv2, uv3) )
5288 b.append( (uv1, uv3, uv4) )
5290 for tidx, tri
in enumerate(tris):
5292 for vidx, idx
in enumerate(tri):
5293 v = mesh.vertices[ idx ]
5296 nx,ny,nz =
swap( v.normal )
5298 nx,ny,nz =
swap( F.normal )
5305 k = list(F.vertices).index(idx)
5306 r,g,b = getattr( vcolors.data[ F.index ],
'color%s'%(k+1) )
5308 ra,ga,ba = getattr( vcolors_alpha.data[ F.index ],
'color%s'%(k+1) )
5315 for layer
in uvtris[ tidx ]:
5316 vert_uvs.append(layer[ vidx ])
5318 ''' Check if we already exported that vertex with same normal, do not export in that case, 5319 (flat shading in blender seems to work with face normals, so we copy each flat face' 5320 vertices, if this vertex with same normals was already exported, 5321 todo: maybe not best solution, check other ways (let blender do all the work, or only 5322 support smooth shading, what about seems, smoothing groups, materials, ...) 5324 vert = VertexNoPos(numverts, nx, ny, nz, r, g, b, ra, vert_uvs)
5325 alreadyExported =
False 5326 if idx
in _sm_vertices_:
5327 for vert2
in _sm_vertices_[idx]:
5330 face.append(vert2.ogre_vidx)
5331 alreadyExported =
True 5334 if not alreadyExported:
5335 face.append(vert.ogre_vidx)
5336 _sm_vertices_[idx].append(vert)
5339 face.append(vert.ogre_vidx)
5340 _sm_vertices_[idx] = [vert]
5347 _remap_verts_.append( v )
5351 doc.start_tag(
'vertex', {})
5352 doc.leaf_tag(
'position', {
5358 doc.leaf_tag(
'normal', {
5365 doc.leaf_tag(
'colour_diffuse', {
'value' :
'%6f %6f %6f %6f' % (r,g,b,ra)})
5370 doc.leaf_tag(
'texcoord', {
5371 'u' : '%6f' % uv[0],
5372 'v' :
'%6f' % (1.0-uv[1])
5375 doc.end_tag(
'vertex')
5377 faces.append( (face[0], face[1], face[2]) )
5379 Report.vertices += numverts
5381 doc.end_tag(
'vertexbuffer')
5382 doc.end_tag(
'sharedgeometry')
5385 print(
' Done at', timer_diff_str(start),
"seconds")
5386 print(
' - Writing submeshes')
5388 doc.start_tag(
'submeshes', {})
5389 for matidx, mat
in enumerate( materials ):
5390 if not len(_sm_faces_[matidx]):
5391 if not isinstance(mat, str):
5395 Report.warnings.append(
'BAD SUBMESH "%s": material %r, has not been applied to any faces - not exporting as submesh.' % (ob.name, mat_name) )
5398 submesh_attributes = {
5399 'usesharedvertices' :
'true',
5402 "use32bitindexes" : str(bool(numverts > 65535)),
5403 "operationtype" :
"triangle_list" 5405 if material_name(mat,
False) !=
"_missing_material_":
5406 submesh_attributes[
'material'] = material_name(mat,
False)
5408 doc.start_tag(
'submesh', submesh_attributes)
5409 doc.start_tag(
'faces', {
5410 'count' : str(len(_sm_faces_[matidx]))
5412 for fidx, (v1, v2, v3)
in enumerate(_sm_faces_[matidx]):
5413 doc.leaf_tag(
'face', {
5418 doc.end_tag(
'faces')
5419 doc.end_tag(
'submesh')
5420 Report.triangles += len(_sm_faces_[matidx])
5424 doc.end_tag(
'submeshes')
5429 doc.start_tag(
'submeshnames', {})
5430 for matidx, mat
in enumerate( materials ):
5431 doc.leaf_tag(
'submesh', {
5432 'name' : material_name(mat,
False),
5433 'index' : str(matidx)
5435 doc.end_tag(
'submeshnames')
5438 print(
' Done at', timer_diff_str(start),
"seconds")
5441 if isLOD ==
False and ob.type ==
'MESH' and CONFIG[
'lodLevels'] > 0:
5442 lod_levels = CONFIG[
'lodLevels']
5443 lod_distance = CONFIG[
'lodDistance']
5444 lod_ratio = CONFIG[
'lodPercent'] / 100.0
5445 lod_pre_mesh_count = len(bpy.data.meshes)
5451 def activate_object(obj):
5452 bpy.ops.object.select_all(action =
'DESELECT')
5453 bpy.context.scene.objects.active = obj
5456 def duplicate_object(scene, name, copyobj):
5459 mesh = bpy.data.meshes.new(name)
5462 ob_new = bpy.data.objects.new(name, mesh)
5465 ob_new.data = copyobj.data.copy()
5466 ob_new.location = copyobj.location
5467 ob_new.rotation_euler = copyobj.rotation_euler
5468 ob_new.scale = copyobj.scale
5471 scene.objects.link(ob_new)
5472 ob_new.select =
True 5476 def delete_object(obj):
5477 activate_object(obj)
5478 bpy.ops.object.delete()
5481 def get_or_create_modifier(obj, modifier_name):
5482 if obj.type !=
'MESH':
5485 for mod_iter
in obj.modifiers:
5486 if mod_iter.type == modifier_name:
5489 activate_object(obj)
5490 bpy.ops.object.modifier_add(type=modifier_name)
5491 return get_or_create_modifier(obj, modifier_name)
5494 ob_copy, ob_copy_mesh = duplicate_object(bpy.context.scene, ob.name +
"_LOD_TEMP_COPY", ob)
5495 ob_copy_meshes = [ ob_copy.data, ob_copy_mesh ]
5498 decimate = get_or_create_modifier(ob_copy,
'DECIMATE')
5499 if decimate
is not None:
5500 decimate.decimate_type =
'COLLAPSE' 5501 decimate.show_viewport =
True 5502 decimate.show_render =
True 5505 lod_ratio_multiplier = 1.0 - lod_ratio
5506 lod_current_ratio = 1.0 * lod_ratio_multiplier
5507 lod_current_distance = lod_distance
5508 lod_current_vertice_count = len(mesh.vertices)
5509 lod_min_vertice_count = 12
5511 for level
in range(lod_levels+1)[1:]:
5512 decimate.ratio = lod_current_ratio
5513 lod_mesh = ob_copy.to_mesh(scene = bpy.context.scene, apply_modifiers =
True, settings =
'PREVIEW')
5514 ob_copy_meshes.append(lod_mesh)
5517 lod_mesh_vertices = len(lod_mesh.vertices)
5518 if lod_mesh_vertices < lod_min_vertice_count:
5519 print(
' - LOD', level,
'vertice count', lod_mesh_vertices,
'too small. Ignoring LOD.')
5521 if lod_mesh_vertices >= lod_current_vertice_count:
5522 print(
' - LOD', level-1,
'vertice count', lod_mesh_vertices,
'cannot be decimated any longer. Ignoring LOD.')
5526 lod_generated.append({
'level': level,
'distance': lod_current_distance,
'ratio': lod_current_ratio,
'mesh': lod_mesh })
5527 lod_current_distance += lod_distance
5528 lod_current_vertice_count = lod_mesh_vertices
5529 lod_current_ratio *= lod_ratio_multiplier
5532 if len(lod_generated) > 0:
5538 doc.start_tag(
'levelofdetail', {
5539 'strategy' :
'default',
5540 'numlevels' : str(len(lod_generated) + 1),
5544 print(
' - Generating', len(lod_generated),
'LOD meshes. Original: vertices', len(mesh.vertices),
"faces", len(mesh.tessfaces))
5545 for lod
in lod_generated:
5546 ratio_percent = round(lod[
'ratio'] * 100.0, 0)
5547 print(
' > Writing LOD', lod[
'level'],
'for distance', lod[
'distance'],
'and ratio', str(ratio_percent) +
"%",
'with', len(lod[
'mesh'].vertices),
'vertices', len(lod[
'mesh'].tessfaces),
'faces')
5548 lod_ob_temp = bpy.data.objects.new(name, lod[
'mesh'])
5549 lod_ob_temp.data.name = name +
'_LOD_' + str(lod[
'level'])
5550 dot_mesh(lod_ob_temp, path, lod_ob_temp.data.name, ignore_shape_animation, normals, isLOD=
True)
5553 doc.leaf_tag(
'lodmanual', {
5554 'value' : str(lod[
'distance']),
5555 'meshname' : lod_ob_temp.data.name +
".mesh" 5560 lod_ob_temp.user_clear()
5561 delete_object(lod_ob_temp)
5564 doc.end_tag(
'levelofdetail')
5567 delete_object(ob_copy)
5571 for mesh_iter
in ob_copy_meshes:
5572 mesh_iter.user_clear()
5573 bpy.data.meshes.remove(mesh_iter)
5577 if lod_pre_mesh_count != len(bpy.data.meshes):
5578 print(
' - WARNING: After LOD generation, cleanup failed to erase all temporary data!')
5580 arm = ob.find_armature()
5582 doc.leaf_tag(
'skeletonlink', {
5583 'name' :
'%s.skeleton' % name
5585 doc.start_tag(
'boneassignments', {})
5586 boneOutputEnableFromName = {}
5587 boneIndexFromName = {}
5588 for bone
in arm.pose.bones:
5589 boneOutputEnableFromName[ bone.name ] =
True 5590 if CONFIG[
'ONLY_DEFORMABLE_BONES']:
5592 if bone.bone.use_deform:
5594 parBone = bone.parent
5596 boneOutputEnableFromName[ parBone.name ] =
True 5597 parBone = parBone.parent
5600 boneOutputEnableFromName[ bone.name ] =
False 5602 for bone
in arm.pose.bones:
5603 boneIndexFromName[ bone.name ] = boneIndex
5604 if boneOutputEnableFromName[ bone.name ]:
5607 for vidx, v
in enumerate(_remap_verts_):
5609 for vgroup
in v.groups:
5610 if vgroup.weight > CONFIG[
'TRIM_BONE_WEIGHTS']:
5611 groupIndex = vgroup.group
5612 if groupIndex < len(copy.vertex_groups):
5613 vg = copy.vertex_groups[ groupIndex ]
5614 if vg.name
in boneIndexFromName:
5615 bnidx = boneIndexFromName[ vg.name ]
5616 doc.leaf_tag(
'vertexboneassignment', {
5617 'vertexindex' : str(vidx),
5618 'boneindex' : str(bnidx),
5619 'weight' :
'%6f' % vgroup.weight
5623 print(
'WARNING: object vertex groups not in sync with armature', copy, arm, groupIndex)
5626 print(
'WARNING: vertex %s is in more than 4 vertex groups (bone weights)\n(this maybe Ogre incompatible)' %vidx)
5628 Report.warnings.append(
'%s has %s vertices weighted to too many bones (Ogre limits a vertex to 4 bones)\n[try increaseing the Trim-Weights threshold option]' %(mesh.name, badverts) )
5629 doc.end_tag(
'boneassignments')
5632 if CONFIG[
'SHAPE_ANIM']
and ob.data.shape_keys
and len(ob.data.shape_keys.key_blocks):
5633 print(
' - Writing shape keys')
5635 doc.start_tag(
'poses', {})
5636 for sidx, skey
in enumerate(ob.data.shape_keys.key_blocks):
5637 if sidx == 0:
continue 5638 if len(skey.data) != len( mesh.vertices ):
5639 failure =
'FAILED to save shape animation - you can not use a modifier that changes the vertex count! ' 5640 failure +=
'[ mesh : %s ]' %mesh.name
5641 Report.warnings.append( failure )
5645 doc.start_tag(
'pose', {
5653 for vidx, v
in enumerate(_remap_verts_):
5654 pv = skey.data[ v.index ]
5655 x,y,z =
swap( pv.co - v.co )
5660 doc.leaf_tag(
'poseoffset', {
5667 doc.end_tag(
'poses')
5671 print(
' Done at', timer_diff_str(start),
"seconds")
5673 if ob.data.shape_keys.animation_data
and len(ob.data.shape_keys.animation_data.nla_tracks):
5674 print(
' - Writing shape animations')
5675 doc.start_tag(
'animations', {})
5676 _fps = float( bpy.context.scene.render.fps )
5677 for nla
in ob.data.shape_keys.animation_data.nla_tracks:
5678 for idx, strip
in enumerate(nla.strips):
5679 doc.start_tag(
'animation', {
5680 'name' : strip.name,
5681 'length' : str((strip.frame_end-strip.frame_start)/_fps)
5683 doc.start_tag(
'tracks', {})
5684 doc.start_tag(
'track', {
5691 doc.start_tag(
'keyframes', {})
5692 for frame
in range( int(strip.frame_start), int(strip.frame_end)+1, bpy.context.scene.frame_step):
5693 bpy.context.scene.frame_set(frame)
5694 doc.start_tag(
'keyframe', {
5695 'time' : str((frame-strip.frame_start)/_fps)
5697 for sidx, skey
in enumerate( ob.data.shape_keys.key_blocks ):
5698 if sidx == 0:
continue 5699 doc.leaf_tag(
'poseref', {
5700 'poseindex' : str(sidx-1),
5701 'influence' : str(skey.value)
5703 doc.end_tag(
'keyframe')
5704 doc.end_tag(
'keyframes')
5705 doc.end_tag(
'track')
5706 doc.end_tag(
'tracks')
5707 doc.end_tag(
'animation')
5708 doc.end_tag(
'animations')
5709 print(
' Done at', timer_diff_str(start),
"seconds")
5716 bpy.data.objects.remove(copy)
5718 bpy.data.meshes.remove(mesh)
5727 print(
' - Created .mesh.xml at', timer_diff_str(start),
"seconds")
5730 def replaceInplace(f,searchExp,replaceExp):
5732 for line
in fileinput.input(f, inplace=1):
5733 if searchExp
in line:
5734 line = line.replace(searchExp,replaceExp)
5735 sys.stdout.write(line)
5738 replaceInplace(xmlfile,
'__TO_BE_REPLACED_VERTEX_COUNT__' +
'"', str(numverts) +
'"' )
5742 OgreXMLConverter(xmlfile, has_uvs=dotextures)
5744 if arm
and CONFIG[
'ARM_ANIM']:
5745 skel = Skeleton( ob )
5746 data = skel.to_xml()
5747 name = force_name
or ob.data.name
5748 name = clean_object_name(name)
5749 xmlfile = os.path.join(path,
'%s.skeleton.xml' % name)
5750 f = open( xmlfile,
'wb' )
5751 f.write( bytes(data,
'utf-8') )
5753 OgreXMLConverter( xmlfile )
5756 for mat
in materials:
5757 if mat !=
'_missing_material_':
5761 print(
' - Created .mesh in total time', timer_diff_str(start),
'seconds')
5767 class JmonkeyPreviewOp( _OgreCommonExport_, bpy.types.Operator ):
5768 '''helper to open jMonkey (JME)''' 5769 bl_idname =
'jmonkey.preview' 5770 bl_label =
"opens JMonkeyEngine in a non-blocking subprocess" 5771 bl_options = {
'REGISTER'}
5773 filepath= StringProperty(name=
"File Path", description=
"Filepath used for exporting Jmonkey .scene file", maxlen=1024, default=
"/tmp/preview.txml", subtype=
'FILE_PATH')
5774 EXPORT_TYPE =
'OGRE' 5777 def poll(cls, context):
5778 if context.active_object:
return True 5780 def invoke(self, context, event):
5781 global TundraSingleton
5782 path =
'/tmp/preview.scene' 5783 self.ogre_export( path, context )
5787 def JmonkeyPipe( path ):
5788 root = CONFIG[
'JMONKEY_ROOT']
5789 if sys.platform.startswith(
'win'):
5790 cmd = [ os.path.join( os.path.join( root,
'bin' ),
'jmonkeyplatform.exe' ) ]
5792 cmd = [ os.path.join( os.path.join( root,
'bin' ),
'jmonkeyplatform' ) ]
5793 cmd.append(
'--nosplash' )
5794 cmd.append(
'--open' )
5796 proc = subprocess.Popen(cmd)
5804 class TundraPreviewOp( _OgreCommonExport_, bpy.types.Operator ):
5805 '''helper to open Tundra2 (realXtend)''' 5806 bl_idname =
'tundra.preview' 5807 bl_label =
"opens Tundra2 in a non-blocking subprocess" 5808 bl_options = {
'REGISTER'}
5811 filepath= StringProperty(
5813 description=
"Filepath used for exporting Tundra .txml file",
5815 default=
"/tmp/preview.txml",
5816 subtype=
'FILE_PATH')
5817 EX_FORCE_CAMERA = BoolProperty(
5818 name=
"Force Camera",
5819 description=
"export active camera",
5821 EX_FORCE_LAMPS = BoolProperty(
5823 description=
"export all lamps",
5827 def poll(cls, context):
5828 if context.active_object
and context.mode !=
'EDIT_MESH':
5831 def invoke(self, context, event):
5832 global TundraSingleton
5836 actob = context.active_object
5837 obs = TundraSingleton.deselect_previously_updated(context)
5840 syncmats.append( ob )
5841 if ob.name == actob.name:
5842 export_mesh( ob, path=
'/tmp/rex' )
5844 if not os.path.isdir(
'/tmp/rex' ): os.makedirs(
'/tmp/rex' )
5845 path =
'/tmp/rex/preview.txml' 5846 self.ogre_export( path, context, force_material_update=syncmats )
5847 if not TundraSingleton:
5848 TundraSingleton = TundraPipe( context )
5850 TundraSingleton.load( context, path )
5856 TundraSingleton =
None 5858 class Tundra_StartPhysicsOp(bpy.types.Operator):
5859 '''TundraSingleton helper''' 5860 bl_idname =
'tundra.start_physics' 5861 bl_label =
"start physics" 5862 bl_options = {
'REGISTER'}
5865 def poll(cls, context):
5866 if TundraSingleton:
return True 5867 def invoke(self, context, event):
5868 TundraSingleton.start()
5871 class Tundra_StopPhysicsOp(bpy.types.Operator):
5872 '''TundraSingleton helper''' 5873 bl_idname =
'tundra.stop_physics' 5874 bl_label =
"stop physics" 5875 bl_options = {
'REGISTER'}
5878 def poll(cls, context):
5879 if TundraSingleton:
return True 5880 def invoke(self, context, event):
5881 TundraSingleton.stop()
5884 class Tundra_PhysicsDebugOp(bpy.types.Operator):
5885 '''TundraSingleton helper''' 5886 bl_idname =
'tundra.toggle_physics_debug' 5887 bl_label =
"stop physics" 5888 bl_options = {
'REGISTER'}
5891 def poll(cls, context):
5892 if TundraSingleton:
return True 5893 def invoke(self, context, event):
5894 TundraSingleton.toggle_physics_debug()
5897 class Tundra_ExitOp(bpy.types.Operator):
5898 '''TundraSingleton helper''' 5899 bl_idname =
'tundra.exit' 5900 bl_label =
"quit tundra" 5901 bl_options = {
'REGISTER'}
5904 def poll(cls, context):
5905 if TundraSingleton:
return True 5906 def invoke(self, context, event):
5907 TundraSingleton.exit()
5913 class Server(object):
5914 def stream( self, o ):
5915 b = pickle.dumps( o, protocol=2 )
5917 n = len( b ); d = STREAM_BUFFER_SIZE - n -4
5918 if n > STREAM_BUFFER_SIZE:
5919 print(
'ERROR: STREAM OVERFLOW:', n )
5922 if n < 10: header =
'000%s' %n
5923 elif n < 100: header =
'00%s' %n
5924 elif n < 1000: header =
'0%s' %n
5925 else: header =
'%s' %n
5926 header = bytes( header,
'utf-8' )
5927 assert len(header) == 4
5928 w = header + b + padding
5929 assert len(w) == STREAM_BUFFER_SIZE
5930 self.buffer.insert(0, w )
5933 def multires_lod( self ):
5935 Ogre builtin LOD sucks for character animation 5937 ob = bpy.context.active_object
5938 cam = bpy.context.scene.camera
5940 if ob
and cam
and ob.type==
'MESH' and ob.use_multires_lod:
5941 delta = bpy.context.active_object.matrix_world.to_translation() - cam.matrix_world.to_translation()
5944 if ob.modifiers
and ob.modifiers[0].type ==
'MULTIRES' and ob.modifiers[0].total_levels > 1:
5945 mod = ob.modifiers[0]
5946 step = ob.multires_lod_range / mod.total_levels
5947 level = mod.total_levels - int( dist / step )
5948 if mod.levels != level: mod.levels = level
5952 LOD = self.multires_lod()
5956 for ob
in bpy.context.selected_objects:
5957 if ob.type
not in (
'MESH',
'LAMP',
'SPEAKER'):
continue 5958 loc, rot, scale = ob.matrix_world.decompose()
5959 loc =
swap(loc).to_tuple()
5960 x,y,z =
swap( rot.to_euler() )
5962 x,y,z =
swap( scale )
5963 scale = ( abs(x), abs(y), abs(z) )
5964 d = { p[
'ID']:
uid(ob), p[
'POSITION']:loc, p[
'ROTATION']:rot, p[
'SCALE']:scale, p[
'TYPE']:p[ob.type] }
5967 if ob.name == bpy.context.active_object.name
and LOD
is not None:
5970 if ob.type ==
'MESH':
5971 arm = ob.find_armature()
5972 if arm
and arm.animation_data
and arm.animation_data.nla_tracks:
5974 d[ p[
'ANIMATIONS'] ] = state = {}
5975 for nla
in arm.animation_data.nla_tracks:
5976 for strip
in nla.strips:
5977 if strip.active: state[ strip.name ] = strip.influence
5979 elif ob.type ==
'LAMP':
5980 d[ p[
'ENERGY'] ] = ob.data.energy
5981 d[ p[
'DISTANCE'] ] = ob.data.distance
5982 elif ob.type ==
'SPEAKER':
5983 d[ p[
'VOLUME'] ] = ob.data.volume
5984 d[ p[
'MUTE'] ] = ob.data.muted
5992 self.socket = sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
5993 host=
'localhost'; port = 9420
5994 sock.connect((host, port))
5995 print(
'SERVER: socket connected', sock)
5997 self.setup_callback( bpy.context )
5999 self.ready = threading._allocate_lock()
6000 self.ID = threading._start_new_thread(
6003 print(
'SERVER: thread started')
6005 def loop(self, none):
6009 if not self.ready.locked(): time.sleep(0.001)
6012 if now - prev > 0.066:
6014 try: actob = bpy.context.active_object
6016 if not actob:
continue 6018 sel = bpy.context.active_object
6020 self.ready.release()
6023 bin = self.buffer.pop()
6025 self.socket.sendall( bin )
6027 print(
'SERVER: send data error')
6030 else: print(
'SERVER: empty buffer' )
6032 self.ready.release()
6033 print(
'SERVER: thread exit')
6035 def threadsafe( self, reg ):
6036 if not TundraSingleton:
return 6037 if not self.ready.locked():
6038 self.ready.acquire()
6040 while self.ready.locked():
6045 def setup_callback( self, context ):
6046 print(
'SERVER: setup frame update callback')
6047 if self._handle:
return self._handle
6048 for area
in bpy.context.window.screen.areas:
6049 if area.type ==
'VIEW_3D':
6050 for reg
in area.regions:
6051 if reg.type ==
'WINDOW':
6053 self._handle = reg.callback_add(self.threadsafe, (reg,),
'PRE_VIEW' )
6057 if not self._handle:
6058 print(
'SERVER: FAILED to setup frame update callback')
6060 def _create_stream_proto():
6062 tags =
'ID NAME POSITION ROTATION SCALE DATA SELECTED TYPE MESH LAMP CAMERA SPEAKER ANIMATIONS DISTANCE ENERGY VOLUME MUTE LOD'.split()
6063 for i,tag
in enumerate( tags ):
6064 proto[ tag ] = chr(i)
6067 STREAM_PROTO = _create_stream_proto()
6068 STREAM_BUFFER_SIZE = 2048
6071 # this file was generated by blender2ogre # 6072 import tundra, socket, select, pickle 6073 STREAM_BUFFER_SIZE = 2048 6074 globals().update( %s ) 6075 E = {} # this is just for debugging from the pyconsole 6078 scn = tundra.Scene().MainCameraScene() 6079 return scn.GetEntityRaw( ID ) 6081 class Client(object): 6083 self.socket = sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 6084 host='localhost'; port = 9420 6085 sock.bind((host, port)) 6086 self._animated = {} # entity ID : { anim-name : weight } 6088 def update(self, delay): 6091 poll = select.select( [ sock ], [], [], 0.01 ) 6092 if not poll[0]: return True 6093 data = sock.recv( STREAM_BUFFER_SIZE ) 6094 assert len(data) == STREAM_BUFFER_SIZE 6096 print( 'blender crashed?' ) 6099 s = data[ 4 : int(header)+4 ] 6100 objects = pickle.loads( s ) 6101 scn = tundra.Scene().MainCameraScene() # replaces GetDefaultScene() 6103 e = scn.GetEntityRaw( ob[ID] ) 6105 x,y,z = ob[POSITION] 6106 e.placeable.SetPosition( x,y,z ) 6108 e.placeable.SetScale( x,y,z ) 6109 #e.placeable.SetOrientation( ob[ROTATION] ) 6111 if ob[TYPE] == LAMP: 6112 e.light.range = ob[ DISTANCE ] 6113 e.light.brightness = ob[ ENERGY ] 6114 #e.light.diffColor = !! not wrapped !! 6115 #e.light.specColor = !! not wrapped !! 6116 elif ob[TYPE] == SPEAKER: 6117 e.sound.soundGain = ob[VOLUME] 6118 #e.sound.soundInnerRadius = 6119 #e.sound.soundOuterRadius = 6120 if ob[MUTE]: e.sound.StopSound() 6121 else: e.sound.PlaySound() # tundra API needs sound.IsPlaying() 6123 if ANIMATIONS in ob: 6124 self.update_animation( e, ob ) 6127 #print( 'LOD', ob[LOD] ) 6128 index = e.id + ob[LOD] + 1 6129 for i in range(1,9): 6130 elod = get_entity( e.id + i ) 6132 if elod.id == index and not elod.placeable.visible: 6133 elod.placeable.visible = True 6134 elif elod.id != index and elod.placeable.visible: 6135 elod.placeable.visible = False 6137 if ob[ID] not in E: E[ ob[ID] ] = e 6139 def update_animation( self, e, ob ): 6140 if ob[ID] not in self._animated: 6141 self._animated[ ob[ID] ] = {} 6142 state = self._animated[ ob[ID] ] 6143 ac = e.animationcontroller 6144 for aname in ob[ ANIMATIONS ]: 6145 if aname not in state: # save weight of new animation 6146 state[ aname ] = ob[ANIMATIONS][aname] # weight 6148 if aname not in ob[ANIMATIONS] and ac.IsAnimationActive( aname ): 6149 ac.StopAnim( aname, '0.0' ) 6150 elif aname in ob[ANIMATIONS]: 6151 weight = ob[ANIMATIONS][aname] 6152 if ac.HasAnimationFinished( aname ): 6153 ac.PlayLoopedAnim( aname, '1.0', 'false' ) # PlayAnim(...) TODO single playback 6154 ok = ac.SetAnimationWeight( aname, weight ) 6155 state[ aname ] = weight 6157 if weight != state[ aname ]: 6158 ok = ac.SetAnimationWeight( aname, weight ) 6159 state[ aname ] = weight 6162 tundra.Frame().connect( 'Updated(float)', client.update ) 6163 print('blender2ogre plugin ok') 6166 class TundraPipe(object):
6167 CONFIG_PATH =
'/tmp/rex/plugins.xml' 6168 TUNDRA_SCRIPT_PATH =
'/tmp/rex/b2ogre_plugin.py' 6169 CONFIG_XML =
'''<?xml version="1.0"?> 6171 <!-- plugins.xml is hardcoded to be the default configuration file to load if another file is not specified on the command line with the "config filename.xml" parameter. --> 6172 <plugin path="OgreRenderingModule" /> 6173 <plugin path="EnvironmentModule" /> <!-- EnvironmentModule depends on OgreRenderingModule --> 6174 <plugin path="PhysicsModule" /> <!-- PhysicsModule depends on OgreRenderingModule and EnvironmentModule --> 6175 <plugin path="TundraProtocolModule" /> <!-- TundraProtocolModule depends on OgreRenderingModule --> 6176 <plugin path="JavascriptModule" /> <!-- JavascriptModule depends on TundraProtocolModule --> 6177 <plugin path="AssetModule" /> <!-- AssetModule depends on TundraProtocolModule --> 6178 <plugin path="AvatarModule" /> <!-- AvatarModule depends on AssetModule and OgreRenderingModule --> 6179 <plugin path="ECEditorModule" /> <!-- ECEditorModule depends on OgreRenderingModule, TundraProtocolModule, OgreRenderingModule and AssetModule --> 6180 <plugin path="SkyXHydrax" /> <!-- SkyXHydrax depends on OgreRenderingModule --> 6181 <plugin path="OgreAssetEditorModule" /> <!-- OgreAssetEditorModule depends on OgreRenderingModule --> 6182 <plugin path="DebugStatsModule" /> <!-- DebugStatsModule depends on OgreRenderingModule, EnvironmentModule and AssetModule --> 6183 <plugin path="SceneWidgetComponents" /> <!-- SceneWidgetComponents depends on OgreRenderingModule and TundraProtocolModule --> 6184 <plugin path="PythonScriptModule" /> 6186 <!-- TODO: Currently the above <plugin> items are loaded in the order they are specified, but below, the jsplugin items are loaded in an undefined order. Use the order specified here as the load order. --> 6187 <!-- NOTE: The startup .js scripts are specified only by base name of the file. Don's specify a path here. Place the startup .js scripts to /bin/jsmodules/startup/. --> 6188 <!-- Important: The file names specified here are case sensitive even on Windows! --> 6189 <jsplugin path="cameraapplication.js" /> 6190 <jsplugin path="FirstPersonMouseLook.js" /> 6191 <jsplugin path="MenuBar.js" /> 6193 <!-- Python plugins --> 6194 <!-- <pyplugin path="lib/apitests.py" /> --> <!-- Runs framework api tests --> 6195 <pyplugin path="%s" /> <!-- shows qt py console. could enable by default when add to menu etc. for controls, now just shows directly when is enabled here --> 6197 <option name="--accept_unknown_http_sources" /> 6198 <option name="--accept_unknown_local_sources" /> 6199 <option name="--fpslimit" value="60" /> 6200 <!-- AssetAPI's file system watcher currently disabled because LocalAssetProvider implements 6201 the same functionality for LocalAssetStorages and HTTPAssetProviders do not yet support live-update. --> 6202 <option name="--nofilewatcher" /> 6204 </Tundra>''' %TUNDRA_SCRIPT_PATH
6206 def __init__(self, context, debug=False):
6207 self._physics_debug =
True 6211 if 'Tundra.exe' in os.listdir( CONFIG[
'TUNDRA_ROOT'] ):
6212 exe = os.path.join( CONFIG[
'TUNDRA_ROOT'],
'Tundra.exe' )
6213 elif 'Tundra' in os.listdir( CONFIG[
'TUNDRA_ROOT'] ):
6214 exe = os.path.join( CONFIG[
'TUNDRA_ROOT'],
'Tundra' )
6218 print(
'ERROR: failed to find Tundra executable')
6220 elif sys.platform.startswith(
'win'):
6223 if exe.endswith(
'.exe'): cmd.append(
'wine')
6226 cmd.append(
'--loglevel')
6229 if CONFIG[
'TUNDRA_STREAMING']:
6230 cmd.append(
'--config' )
6231 cmd.append( self.CONFIG_PATH )
6232 with open( self.CONFIG_PATH,
'wb' )
as fp: fp.write( bytes(self.CONFIG_XML,
'utf-8') )
6233 with open( self.TUNDRA_SCRIPT_PATH,
'wb' )
as fp: fp.write( bytes(TUNDRA_SCRIPT,
'utf-8') )
6234 self.server = Server()
6237 cmd.append(
'--storage' )
6238 if sys.platform.startswith(
'win'): cmd.append(
'C:\\tmp\\rex' )
6239 else: cmd.append(
'/tmp/rex' )
6240 self.proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, cwd=CONFIG[
'TUNDRA_ROOT'])
6245 self.load( context,
'/tmp/rex/preview.txml' )
6248 def deselect_previously_updated(self, context):
6250 for ob
in context.selected_objects:
6251 if ob.name
in self._objects: ob.select =
False; r.append( ob )
6254 def load( self, context, url, clear=False ):
6255 self._objects += [ob.name
for ob
in context.selected_objects]
6257 self.proc.stdin.write( b
'loadscene(/tmp/rex/preview.txml,true,true)\n')
6259 self.proc.stdin.write( b
'loadscene(/tmp/rex/preview.txml,false,true)\n')
6261 self.proc.stdin.flush()
6263 global TundraSingleton
6264 TundraSingleton =
None 6268 self.proc.stdin.write( b
'startphysics\n' )
6269 try: self.proc.stdin.flush()
6271 global TundraSingleton
6272 TundraSingleton =
None 6275 self.physics =
False 6276 self.proc.stdin.write( b
'stopphysics\n' )
6277 try: self.proc.stdin.flush()
6279 global TundraSingleton
6280 TundraSingleton =
None 6282 def toggle_physics_debug( self ):
6283 self._physics_debug =
not self._physics_debug
6284 self.proc.stdin.write( b
'physicsdebug\n' )
6285 try: self.proc.stdin.flush()
6287 global TundraSingleton
6288 TundraSingleton =
None 6291 self.proc.stdin.write( b
'exit\n' )
6292 self.proc.stdin.flush()
6293 global TundraSingleton
6294 TundraSingleton =
None 6299 class MENU_preview_material_text(bpy.types.Menu):
6300 bl_label =
'preview' 6303 def poll(self,context):
6304 if context.active_object
and context.active_object.active_material:
6307 def draw(self, context):
6308 layout = self.layout
6309 mat = context.active_object.active_material
6312 preview = generate_material( mat )
6313 for line
in preview.splitlines():
6315 for ww
in wordwrap( line ):
6316 layout.label(text=ww)
6319 class INFO_HT_myheader(bpy.types.Header):
6320 bl_space_type =
'INFO' 6321 def draw(self, context):
6322 layout = self.layout
6323 wm = context.window_manager
6324 window = context.window
6325 scene = context.scene
6327 ob = context.active_object
6328 screen = context.screen
6333 row = layout.row(align=
True)
6334 op = row.operator(
'jmonkey.preview', text=
'', icon=
'MONKEY' )
6337 row = layout.row(align=
True)
6338 op = row.operator(
'tundra.preview', text=
'', icon=
'WORLD' )
6340 op = row.operator(
'tundra.preview', text=
'', icon=
'META_CUBE' )
6342 if not TundraSingleton.physics:
6343 op = row.operator(
'tundra.start_physics', text=
'', icon=
'PLAY' )
6345 op = row.operator(
'tundra.stop_physics', text=
'', icon=
'PAUSE' )
6346 op = row.operator(
'tundra.toggle_physics_debug', text=
'', icon=
'MOD_PHYSICS' )
6347 op = row.operator(
'tundra.exit', text=
'', icon=
'CANCEL' )
6349 op = layout.operator(
'ogremeshy.preview', text=
'', icon=
'PLUGIN' ); op.mesh =
True 6351 row = layout.row(align=
True)
6352 sub = row.row(align=
True)
6353 sub.menu(
"INFO_MT_file")
6354 sub.menu(
"INFO_MT_add")
6355 if rd.use_game_engine: sub.menu(
"INFO_MT_game")
6356 else: sub.menu(
"INFO_MT_render")
6358 row = layout.row(align=
False); row.scale_x = 1.25
6359 row.menu(
"INFO_MT_instances", icon=
'NODETREE', text=
'')
6360 row.menu(
"INFO_MT_groups", icon=
'GROUP', text=
'')
6362 layout.template_header()
6363 if not context.area.show_menus:
6364 if window.screen.show_fullscreen: layout.operator(
"screen.back_to_previous", icon=
'SCREEN_BACK', text=
"Back to Previous")
6365 else: layout.template_ID(context.window,
"screen", new=
"screen.new", unlink=
"screen.delete")
6366 layout.template_ID(context.screen,
"scene", new=
"scene.new", unlink=
"scene.delete")
6369 layout.template_running_jobs()
6370 layout.template_reports_banner()
6372 if rd.has_multiple_engines: layout.prop(rd,
"engine", text=
"")
6374 layout.label(text=scene.statistics())
6375 layout.menu(
"INFO_MT_help" )
6377 layout.template_ID(context.window,
"screen", new=
"screen.new", unlink=
"screen.delete")
6380 row = layout.row(align=
True)
6381 row.prop( ob,
'name', text=
'' )
6382 row.prop( ob,
'draw_type', text=
'' )
6383 row.prop( ob,
'show_x_ray', text=
'' )
6385 row.scale_y = 0.75; row.scale_x = 0.9
6386 row.prop( ob,
'layers', text=
'' )
6389 row = layout.row(align=
True); row.scale_x = 1.1
6390 row.prop(scene.game_settings,
'material_mode', text=
'')
6391 row.prop(scene,
'camera', text=
'')
6393 layout.menu(
'MENU_preview_material_text', icon=
'TEXT', text=
'' )
6395 layout.menu(
"INFO_MT_ogre_docs" )
6396 layout.operator(
"wm.window_fullscreen_toggle", icon=
'FULLSCREEN_ENTER', text=
"")
6397 if OgreToggleInterfaceOp.TOGGLE: layout.operator(
'ogre.toggle_interface', text=
'Ogre', icon=
'CHECKBOX_DEHLT')
6398 else: layout.operator(
'ogre.toggle_interface', text=
'Ogre', icon=
'CHECKBOX_HLT')
6400 def export_menu_func_ogre(self, context):
6401 op = self.layout.operator(INFO_OT_createOgreExport.bl_idname, text=
"Ogre3D (.scene and .mesh)")
6403 def export_menu_func_realxtend(self, context):
6404 op = self.layout.operator(INFO_OT_createRealxtendExport.bl_idname, text=
"realXtend Tundra (.txml and .mesh)")
6407 _header_ = bpy.types.INFO_HT_header
6409 print(
'---blender2ogre addon enable---')
6413 class OgreToggleInterfaceOp(bpy.types.Operator):
6414 '''Toggle Ogre UI''' 6415 bl_idname =
"ogre.toggle_interface" 6416 bl_label =
"Ogre UI" 6417 bl_options = {
'REGISTER'}
6419 BLENDER_DEFAULT_HEADER = _header_
6422 def poll(cls, context):
6425 def invoke(self, context, event):
6427 if OgreToggleInterfaceOp.TOGGLE:
6428 print(
'ogre.toggle_interface ENABLE' )
6429 bpy.utils.register_module(__name__)
6431 try: bpy.utils.unregister_class(_header_)
6433 bpy.utils.unregister_class( INFO_HT_microheader )
6434 OgreToggleInterfaceOp.TOGGLE =
False 6436 print(
'ogre.toggle_interface DISABLE' )
6439 bpy.utils.register_class(_header_)
6440 restore_minimal_interface()
6441 OgreToggleInterfaceOp.TOGGLE =
True 6444 class INFO_HT_microheader(bpy.types.Header):
6445 bl_space_type =
'INFO' 6446 def draw(self, context):
6447 layout = self.layout
6449 if OgreToggleInterfaceOp.TOGGLE:
6450 layout.operator(
'ogre.toggle_interface', text=
'Ogre', icon=
'CHECKBOX_DEHLT')
6452 layout.operator(
'ogre.toggle_interface', text=
'Ogre', icon=
'CHECKBOX_HLT')
6455 def get_minimal_interface_classes():
6456 return INFO_OT_createOgreExport, INFO_OT_createRealxtendExport, OgreToggleInterfaceOp, MiniReport, INFO_HT_microheader
6458 _USE_TUNDRA_ =
False 6459 _USE_JMONKEY_ =
False 6461 def restore_minimal_interface():
6463 for cls
in get_minimal_interface_classes():
6464 try: bpy.utils.register_class( cls )
6469 bpy.utils.register_class( INFO_HT_microheader )
6470 for op
in get_minimal_interface_classes(): bpy.utils.register_class( op )
6473 print(
'b2ogre minimal UI already setup' )
6479 print(
'Starting blender2ogre', VERSION)
6480 global MyShaders, _header_, _USE_TUNDRA_, _USE_JMONKEY_
6484 restore_minimal_interface()
6487 if os.path.isdir( CONFIG[
'TUNDRA_ROOT'] ): _USE_TUNDRA_ =
True 6488 else: _USE_TUNDRA_ =
False 6492 bpy.types.INFO_MT_file_export.append(export_menu_func_ogre)
6493 bpy.types.INFO_MT_file_export.append(export_menu_func_realxtend)
6495 bpy.utils.register_class(PopUpDialogOperator)
6497 if os.path.isdir( CONFIG[
'USER_MATERIALS'] ):
6498 scripts,progs = update_parent_material_path( CONFIG[
'USER_MATERIALS'] )
6500 print(
'Ogre shader program', prog.name)
6502 print(
'[WARNING]: Invalid my-shaders path %s' % CONFIG[
'USER_MATERIALS'])
6505 print(
'Unloading blender2ogre', VERSION)
6506 bpy.utils.unregister_module(__name__)
6507 try: bpy.utils.register_class(_header_)
6512 OgreToggleInterfaceOp.TOGGLE =
True 6513 bpy.types.INFO_MT_file_export.remove(export_menu_func_ogre)
6514 bpy.types.INFO_MT_file_export.remove(export_menu_func_realxtend)
6524 bpy.types.World.ogre_skyX = BoolProperty(
6525 name=
"enable sky", description=
"ogre sky",
6528 bpy.types.World.ogre_skyX_time = FloatProperty(
6529 name=
"Time Multiplier",
6530 description=
"change speed of day/night cycle",
6534 bpy.types.World.ogre_skyX_wind = FloatProperty(
6535 name=
"Wind Direction",
6536 description=
"change direction of wind",
6540 bpy.types.World.ogre_skyX_volumetric_clouds = BoolProperty(
6541 name=
"volumetric clouds", description=
"toggle ogre volumetric clouds",
6544 bpy.types.World.ogre_skyX_cloud_density_x = FloatProperty(
6545 name=
"Cloud Density X",
6546 description=
"change density of volumetric clouds on X",
6550 bpy.types.World.ogre_skyX_cloud_density_y = FloatProperty(
6551 name=
"Cloud Density Y",
6552 description=
"change density of volumetric clouds on Y",
6560 class OgreSkyPanel(bpy.types.Panel):
6561 bl_space_type =
'PROPERTIES' 6562 bl_region_type =
'WINDOW' 6563 bl_context =
"world" 6564 bl_label =
"Ogre Sky Settings" 6567 def poll(cls, context):
6570 def draw(self, context):
6571 layout = self.layout
6573 box.prop( context.world,
'ogre_skyX' )
6574 if context.world.ogre_skyX:
6575 box.prop( context.world,
'ogre_skyX_time' )
6576 box.prop( context.world,
'ogre_skyX_wind' )
6577 box.prop( context.world,
'ogre_skyX_volumetric_clouds' )
6578 if context.world.ogre_skyX_volumetric_clouds:
6579 box.prop( context.world,
'ogre_skyX_cloud_density_x' )
6580 box.prop( context.world,
'ogre_skyX_cloud_density_y' )
6583 class OgreProgram(object):
6585 parses .program scripts 6586 saves bytes to copy later 6588 self.name = name of program reference 6589 self.source = name of shader program (.cg, .glsl) 6592 def save( self, path ):
6593 print(
'saving program to', path)
6594 f = open( os.path.join(path,self.source),
'wb' )
6595 f.write(self.source_bytes )
6597 for name
in self.includes:
6598 f = open( os.path.join(path,name),
'wb' )
6599 f.write( self.includes[name] )
6605 if self.source
not in os.listdir( CONFIG[
'SHADER_PROGRAMS'] ):
6606 print(
'ERROR: ogre material %s is missing source: %s' %(self.name,self.source) )
6607 print( CONFIG[
'SHADER_PROGRAMS'] )
6609 url = os.path.join( CONFIG[
'SHADER_PROGRAMS'], self.source )
6610 print(
'shader source:', url)
6611 self.source_bytes = open( url,
'rb' ).read()
6612 print(
'shader source num bytes:', len(self.source_bytes))
6613 data = self.source_bytes.decode(
'utf-8')
6615 for line
in data.splitlines():
6616 if line.startswith(
'#include')
and line.count(
'"')==2:
6617 name = line.split()[-1].replace(
'"',
'').strip()
6618 print(
'shader includes:', name)
6619 url = os.path.join( CONFIG[
'SHADER_PROGRAMS'], name )
6620 self.includes[ name ] = open( url,
'rb' ).read()
6623 def __init__(self, name='', data=''):
6625 self.data = data.strip()
6629 if self.name
in OgreProgram.PROGRAMS:
6630 print(
'---copy ogreprogram---', self.name)
6631 other = OgreProgram.PROGRAMS
6632 self.source = other.source
6633 self.data = other.data
6634 self.entry_point = other.entry_point
6635 self.profiles = other.profiles
6637 if data: self.parse( self.data )
6638 if self.name: OgreProgram.PROGRAMS[ self.name ] = self
6640 def parse( self, txt ):
6642 print(
'--parsing ogre shader program--' )
6643 for line
in self.data.splitlines():
6645 line = line.split(
'//')[0]
6647 if line.startswith(
'vertex_program')
or line.startswith(
'fragment_program'):
6648 a, self.name, self.type = line.split()
6650 elif line.startswith(
'source'): self.source = line.split()[-1]
6651 elif line.startswith(
'entry_point'): self.entry_point = line.split()[-1]
6652 elif line.startswith(
'profiles'): self.profiles = line.split()[1:]
6656 class OgreMaterialScript(object):
6657 def get_programs(self):
6659 for name
in list(self.vertex_programs.keys()) + list(self.fragment_programs.keys()):
6660 p = get_shader_program( name )
6661 if p: progs.append( p )
6664 def __init__(self, txt, url):
6666 self.data = txt.strip()
6668 self.vertex_programs = {}
6669 self.fragment_programs = {}
6670 self.texture_units = {}
6671 self.texture_units_order = []
6674 line = self.data.splitlines()[0]
6675 assert line.startswith(
'material')
6677 line, self.parent = line.split(
':')
6678 self.name = line.split()[-1]
6679 print(
'new ogre material: %s' %self.name )
6682 self.techniques = techs = []
6685 for line
in self.data.splitlines():
6688 line = line.split(
'//')[0]
6690 if not line:
continue 6692 if line ==
'{': brace += 1
6693 elif line ==
'}': brace -= 1; prog =
None; tex =
None 6695 if line.startswith(
'technique' ):
6696 tech = {
'passes':[]}; techs.append( tech )
6697 if len(line.split()) > 1: tech[
'technique-name'] = line.split()[-1]
6699 if line.startswith(
'pass'):
6700 P = {
'texture_units':[],
'vprogram':
None,
'fprogram':
None,
'body':[]}
6701 tech[
'passes'].append( P )
6702 self.passes.append( P )
6704 elif tech[
'passes']:
6705 P = tech[
'passes'][-1]
6706 P[
'body'].append( rawline )
6708 if line ==
'{' or line ==
'}':
continue 6710 if line.startswith(
'vertex_program_ref'):
6711 prog = P[
'vprogram'] = {
'name':line.split()[-1],
'params':{}}
6712 self.vertex_programs[ prog[
'name'] ] = prog
6713 elif line.startswith(
'fragment_program_ref'):
6714 prog = P[
'fprogram'] = {
'name':line.split()[-1],
'params':{}}
6715 self.fragment_programs[ prog[
'name'] ] = prog
6717 elif line.startswith(
'texture_unit'):
6719 tex = {
'name':line.split()[-1],
'params':{}}
6720 if tex[
'name'] ==
'texture_unit':
6721 print(
'WARNING: material %s contains unnamed texture_units' %self.name)
6722 print(
'---unnamed texture units will be ignored---')
6724 P[
'texture_units'].append( tex )
6725 self.texture_units[ tex[
'name'] ] = tex
6726 self.texture_units_order.append( tex[
'name'] )
6730 if p==
'param_named':
6731 items = line.split()
6732 if len(items) == 4: p, o, t, v = items
6733 elif len(items) == 3:
6736 elif len(items) > 4:
6737 o = items[1]; t = items[2]
6740 opt = {
'name': o,
'type':t,
'raw_value':v }
6741 prog[
'params'][ o ] = opt
6742 if t==
'float': opt[
'value'] = float(v)
6743 elif t
in 'float2 float3 float4'.split(): opt[
'value'] = [ float(a)
for a
in v ]
6744 else: print(
'unknown type:', t)
6747 tex[
'params'][ line.split()[0] ] = line.split()[ 1 : ]
6749 for P
in self.passes:
6751 while lines
and ''.join(lines).count(
'{')!=
''.join(lines).count(
'}'):
6752 if lines[-1].strip() ==
'}': lines.pop()
6754 P[
'body'] =
'\n'.join( lines )
6755 assert P[
'body'].count(
'{') == P[
'body'].count(
'}')
6758 self.hidden_texture_units = rem = []
6759 for tex
in self.texture_units.values():
6760 if 'texture' not in tex[
'params']:
6763 print(
'WARNING: not using texture_unit because it lacks a "texture" parameter', tex[
'name'])
6764 self.texture_units.pop( tex[
'name'] )
6766 if len(self.techniques)>1:
6767 print(
'WARNING: user material %s has more than one technique' %self.url)
6769 def as_abstract_passes( self ):
6771 for i,P
in enumerate(self.passes):
6772 head =
'abstract pass %s/PASS%s' %(self.name,i)
6773 r.append( head +
'\n' + P[
'body'] )
6776 class MaterialScripts(object):
6780 def __init__(self, url):
6783 data = open( url,
'rb' ).read()
6785 self.data = data.decode(
'utf-8')
6787 self.data = data.decode(
'latin-1')
6794 for line
in self.data.splitlines():
6795 if not line.strip():
continue 6798 mat = []; mats.append( mat )
6800 elif a
in (
'vertex_program',
'fragment_program',
'abstract'):
6802 elif mat
and not skip:
6804 elif skip
and line==
'}':
6809 omat = OgreMaterialScript(
'\n'.join( mat ), url )
6810 if omat.name
in self.ALL_MATERIALS:
6811 print(
'WARNING: material %s redefined' %omat.name )
6816 self.materials[ omat.name ] = omat
6817 self.ALL_MATERIALS[ omat.name ] = omat
6818 if omat.vertex_programs
or omat.fragment_programs:
6819 self.ENUM_ITEMS.append( (omat.name, omat.name, url) )
6822 def reset_rna(self, callback=None):
6823 bpy.types.Material.ogre_parent_material = EnumProperty(
6824 name=
"Script Inheritence",
6825 description=
'ogre parent material class',
6826 items=self.ENUM_ITEMS,
6832 def is_image_postprocessed( image ):
6833 if CONFIG[
'FORCE_IMAGE_FORMAT'] !=
'NONE' or image.use_resize_half
or image.use_resize_absolute
or image.use_color_quantize
or image.use_convert_format:
6838 class _image_processing_( object ):
6839 def _reformat( self, name, image ):
6840 if image.convert_format !=
'NONE':
6841 name =
'%s.%s' %(name[:name.rindex(
'.')], image.convert_format)
6842 if image.convert_format ==
'dds': name =
'_DDS_.%s' %name
6843 elif image.use_resize_half
or image.use_resize_absolute
or image.use_color_quantize
or image.use_convert_format:
6844 name =
'_magick_.%s' %name
6845 if CONFIG[
'FORCE_IMAGE_FORMAT'] !=
'NONE' and not name.endswith(
'.dds'):
6846 name =
'%s.%s' %(name[:name.rindex(
'.')], CONFIG[
'FORCE_IMAGE_FORMAT'])
6847 if CONFIG[
'FORCE_IMAGE_FORMAT'] ==
'dds':
6848 name =
'_DDS_.%s' %name
6851 def image_magick( self, texture, infile ):
6852 print(
'IMAGE MAGICK', infile )
6853 exe = CONFIG[
'IMAGE_MAGICK_CONVERT']
6854 if not os.path.isfile( exe ):
6855 Report.warnings.append(
'ImageMagick not installed!' )
6856 print(
'ERROR: can not find Image Magick - convert', exe );
return 6857 cmd = [ exe, infile ]
6859 x,y = texture.image.size
6860 ax = texture.image.resize_x
6861 ay = texture.image.resize_y
6863 if texture.image.use_convert_format
and texture.image.convert_format ==
'jpg':
6864 cmd.append(
'-quality' )
6865 cmd.append(
'%s' %texture.image.jpeg_quality )
6867 if texture.image.use_resize_half:
6868 cmd.append(
'-resize' )
6869 cmd.append(
'%sx%s' %(x/2, y/2) )
6870 elif texture.image.use_resize_absolute
and (x>ax
or y>ay):
6871 cmd.append(
'-resize' )
6872 cmd.append(
'%sx%s' %(ax,ay) )
6874 elif x > CONFIG[
'MAX_TEXTURE_SIZE']
or y > CONFIG[
'MAX_TEXTURE_SIZE']:
6875 cmd.append(
'-resize' )
6876 cmd.append( str(CONFIG[
'MAX_TEXTURE_SIZE']) )
6878 if texture.image.use_color_quantize:
6879 if texture.image.use_color_quantize_dither:
6880 cmd.append(
'+dither' )
6881 cmd.append(
'-colors' )
6882 cmd.append( str(texture.image.color_quantize) )
6884 path,name = os.path.split( infile )
6886 outfile = os.path.join( path, self._reformat(name,texture.image) )
6887 if outfile.endswith(
'.dds'):
6888 temp = os.path.join( path,
'_temp_.png' )
6890 print(
'IMAGE MAGICK: %s' %cmd )
6891 subprocess.call( cmd )
6892 self.nvcompress( texture, temp, outfile=outfile )
6895 cmd.append( outfile )
6896 print(
'IMAGE MAGICK: %s' %cmd )
6897 subprocess.call( cmd )
6899 def nvcompress(self, texture, infile, outfile=None, version=1, fast=False, blocking=True):
6900 print(
'[NVCompress DDS Wrapper]', infile )
6901 assert version
in (1,2,3,4,5)
6902 exe = CONFIG[
'NVCOMPRESS']
6905 if texture.image.use_alpha
and texture.image.depth==32:
6906 cmd.append(
'-alpha' )
6907 if not texture.use_mipmap:
6908 cmd.append(
'-nomips' )
6910 if texture.use_normal_map:
6911 cmd.append(
'-normal' )
6912 if version
in (1,3):
6913 cmd.append(
'-bc%sn' %version )
6915 cmd.append(
'-bc%s' %version )
6917 cmd.append(
'-bc%s' %version )
6920 cmd.append(
'-fast' )
6921 cmd.append( infile )
6923 if outfile: cmd.append( outfile )
6927 subprocess.call( cmd )
6929 subprocess.Popen( cmd )
6933 _nvcompress_doc =
''' 6934 usage: nvcompress [options] infile [outfile] 6937 -color The input image is a color map (default). 6938 -alpha The input image has an alpha channel used for transparency. 6939 -normal The input image is a normal map. 6940 -tonormal Convert input to normal map. 6941 -clamp Clamp wrapping mode (default). 6942 -repeat Repeat wrapping mode. 6943 -nomips Disable mipmap generation. 6945 Compression options: 6946 -fast Fast compression. 6947 -nocuda Do not use cuda compressor. 6949 -bc1 BC1 format (DXT1) 6950 -bc1n BC1 normal map format (DXT1nm) 6951 -bc1a BC1 format with binary alpha (DXT1a) 6952 -bc2 BC2 format (DXT3) 6953 -bc3 BC3 format (DXT5) 6954 -bc3n BC3 normal map format (DXT5nm) 6955 -bc4 BC4 format (ATI1) 6956 -bc5 BC5 format (3Dc/ATI2) 6959 class OgreMaterialGenerator( _image_processing_ ):
6960 def __init__(self, material, path='/tmp', touch_textures=False ):
6961 self.material = material
6964 self.touch_textures = touch_textures
6965 if material.node_tree:
6966 nodes = bpyShaders.get_subnodes( self.material.node_tree, type=
'MATERIAL_EXT' )
6969 self.passes.append( node.material )
6971 def get_active_programs(self):
6973 for mat
in self.passes:
6974 if mat.use_ogre_parent_material
and mat.ogre_parent_material:
6975 usermat = get_ogre_user_material( mat.ogre_parent_material )
6976 for prog
in usermat.get_programs(): r.append( prog )
6979 def get_header(self):
6981 for mat
in self.passes:
6982 if mat.use_ogre_parent_material
and mat.ogre_parent_material:
6983 usermat = get_ogre_user_material( mat.ogre_parent_material )
6984 r.append(
'// user material: %s' %usermat.name )
6985 for prog
in usermat.get_programs():
6986 r.append( prog.data )
6987 r.append(
'// abstract passes //' )
6988 r += usermat.as_abstract_passes()
6989 return '\n'.join( r )
6991 def get_passes(self):
6993 r.append( self.generate_pass(self.material) )
6994 for mat
in self.passes:
6995 if mat.use_in_ogre_material_pass:
6996 r.append( self.generate_pass(mat) )
6999 def generate_pass( self, mat, pass_name=None ):
7000 usermat = texnodes =
None 7001 if mat.use_ogre_parent_material
and mat.ogre_parent_material:
7002 usermat = get_ogre_user_material( mat.ogre_parent_material )
7003 texnodes = bpyShaders.get_texture_subnodes( self.material, mat )
7006 if not pass_name: pass_name = mat.name
7008 M += indent(2,
'pass %s : %s/PASS0' %(pass_name,usermat.name),
'{' )
7010 M += indent(2,
'pass %s'%pass_name,
'{' )
7012 color = mat.diffuse_color
7014 if mat.use_transparency:
7017 slots = get_image_textures( mat )
7021 if (slot.texture.image
is not None)
and (slot.texture.image.use_alpha): usealpha =
True;
break 7026 if mat.use_fixed_pipeline:
7028 if mat.use_vertex_color_paint:
7029 M += indent(3,
'ambient vertexcolour' )
7031 M += indent(3,
'ambient %s %s %s %s' %(color.r*f, color.g*f, color.b*f, alpha) )
7033 f = mat.diffuse_intensity
7034 if mat.use_vertex_color_paint:
7035 M += indent(3,
'diffuse vertexcolour' )
7037 M += indent(3,
'diffuse %s %s %s %s' %(color.r*f, color.g*f, color.b*f, alpha) )
7039 f = mat.specular_intensity
7040 s = mat.specular_color
7041 M += indent(3,
'specular %s %s %s %s %s' %(s.r*f, s.g*f, s.b*f, alpha, mat.specular_hardness/4.0) )
7044 if mat.use_shadeless:
7045 M += indent(3,
'emissive %s %s %s 1.0' %(color.r, color.g, color.b) )
7046 elif mat.use_vertex_color_light:
7047 M += indent(3,
'emissive vertexcolour' )
7049 M += indent(3,
'emissive %s %s %s %s' %(color.r*f, color.g*f, color.b*f, alpha) )
7053 M += indent(3,
'depth_bias %s'%mat.offset_z )
7055 for name
in dir(mat):
7056 if name.startswith(
'ogre_')
and name !=
'ogre_parent_material':
7057 var = getattr(mat,name)
7058 op = name.replace(
'ogre_',
'')
7060 if type(var) == bool:
7063 M += indent( 3,
'%s %s' %(op,val) )
7066 if texnodes
and usermat.texture_units:
7067 for i,name
in enumerate(usermat.texture_units_order):
7071 geo = bpyShaders.get_connected_input_nodes( self.material, node )[0]
7072 M += self.generate_texture_unit( node.texture, name=name, uv_layer=geo.uv_layer )
7075 M += self.generate_texture_unit( slot.texture, slot=slot )
7077 M += indent(2,
'}' )
7080 def generate_texture_unit(self, texture, slot=None, name=None, uv_layer=None):
7081 if not hasattr(texture,
'image'):
7082 print(
'WARNING: texture must be of type IMAGE->', texture)
7084 if not texture.image:
7085 print(
'WARNING: texture has no image assigned->', texture)
7088 if slot
and not slot.use:
return '' 7092 M =
''; _alphahack =
None 7093 if not name: name =
'' 7094 M += indent(3,
'texture_unit %s' %name,
'{' )
7097 libpath = os.path.split( bpy.path.abspath(texture.library.filepath) )[0]
7098 iurl = bpy.path.abspath( texture.image.filepath, libpath )
7100 iurl = bpy.path.abspath( texture.image.filepath )
7102 postname = texname = os.path.split(iurl)[-1]
7105 if texture.image.packed_file:
7106 orig = texture.image.filepath
7107 iurl = os.path.join(path, texname)
7109 print(
'WARNING: packed image is of unknown type - assuming PNG format')
7111 texname = postname = os.path.split(iurl)[-1]
7113 if not os.path.isfile( iurl ):
7114 if self.touch_textures:
7115 print(
'MESSAGE: unpacking image: ', iurl)
7116 texture.image.filepath = iurl
7117 texture.image.save()
7118 texture.image.filepath = orig
7120 print(
'MESSAGE: packed image already in temp, not updating', iurl)
7122 if is_image_postprocessed( texture.image ):
7123 postname = self._reformat( texname, texture.image )
7124 print(
'MESSAGE: image postproc',postname)
7126 M += indent(4,
'texture %s' %postname )
7128 exmode = texture.extension
7129 if exmode
in TextureUnit.tex_address_mode:
7130 M += indent(4,
'tex_address_mode %s' %TextureUnit.tex_address_mode[exmode] )
7135 if exmode ==
'CLIP': M += indent(4,
'tex_border_colour %s %s %s' %(slot.color.r, slot.color.g, slot.color.b) )
7136 M += indent(4,
'scale %s %s' %(1.0/slot.scale.x, 1.0/slot.scale.y) )
7137 if slot.texture_coords ==
'REFLECTION':
7138 if slot.mapping ==
'SPHERE':
7139 M += indent(4,
'env_map spherical' )
7140 elif slot.mapping ==
'FLAT':
7141 M += indent(4,
'env_map planar' )
7142 else: print(
'WARNING: <%s> has a non-UV mapping type (%s) and not picked a proper projection type of: Sphere or Flat' %(texture.name, slot.mapping))
7144 ox,oy,oz = slot.offset
7146 M += indent(4,
'scroll %s %s' %(ox,oy) )
7148 M += indent(4,
'rotate %s' %oz )
7151 if slot.use_from_dupli:
7152 M += indent(4,
'rotate_anim %s' %slot.emission_color_factor )
7153 if slot.use_map_scatter:
7154 M += indent(4,
'scroll_anim %s %s ' %(slot.density_factor, slot.emission_factor) )
7157 idx = find_uv_layer_index( slot.uv_layer, self.material )
7158 M += indent(4,
'tex_coord_set %s' %idx)
7161 if texture.image.depth == 32: rgba =
True 7162 btype = slot.blend_type
7163 ex =
False; texop =
None 7164 if btype
in TextureUnit.colour_op:
7165 if btype==
'MIX' and slot.use_map_alpha
and not slot.use_stencil:
7166 if slot.diffuse_color_factor >= 1.0: texop =
'alpha_blend' 7168 texop = TextureUnit.colour_op[ btype ]
7170 elif btype==
'MIX' and slot.use_map_alpha
and slot.use_stencil:
7171 texop =
'blend_current_alpha'; ex=
True 7172 elif btype==
'MIX' and not slot.use_map_alpha
and slot.use_stencil:
7173 texop =
'blend_texture_alpha'; ex=
True 7175 texop = TextureUnit.colour_op[ btype ]
7176 elif btype
in TextureUnit.colour_op_ex:
7177 texop = TextureUnit.colour_op_ex[ btype ]
7181 if texop ==
'blend_manual':
7182 factor = 1.0 - slot.diffuse_color_factor
7183 M += indent(4,
'colour_op_ex %s src_texture src_current %s' %(texop, factor) )
7185 M += indent(4,
'colour_op_ex %s src_texture src_current' %texop )
7187 M += indent(4,
'colour_op %s' %texop )
7191 idx = find_uv_layer_index( uv_layer )
7192 M += indent(4,
'tex_coord_set %s' %idx)
7194 M += indent(3,
'}' )
7196 if self.touch_textures:
7198 if not os.path.isfile(iurl):
7199 Report.warnings.append(
'Missing texture: %s' %iurl )
7201 desturl = os.path.join( destpath, texname )
7203 if not os.path.isfile( desturl )
or os.stat( desturl ).st_mtime < os.stat( iurl ).st_mtime:
7204 f = open( desturl,
'wb' )
7205 f.write( open(iurl,
'rb').read() )
7208 posturl = os.path.join(destpath,postname)
7209 if is_image_postprocessed( texture.image ):
7210 if not os.path.isfile( posturl )
or updated:
7211 self.image_magick( texture, desturl )
7215 class TextureUnit(object):
7219 'MULTIPLY' :
'modulate',
7223 'MIX' :
'blend_manual',
7224 'SCREEN':
'modulate_x2',
7225 'LIGHTEN':
'modulate_x4',
7226 'SUBTRACT':
'subtract',
7227 'OVERLAY':
'add_signed',
7228 'DIFFERENCE':
'dotproduct',
7229 'VALUE':
'blend_diffuse_colour',
7232 tex_address_mode = {
7236 'CHECKER' :
'mirror' 7241 class PANEL_Object(bpy.types.Panel):
7242 bl_space_type =
'PROPERTIES' 7243 bl_region_type =
'WINDOW' 7244 bl_context =
"object" 7245 bl_label =
"Object+" 7248 def poll(cls, context):
7249 if _USE_TUNDRA_
and context.active_object:
7252 def draw(self, context):
7253 ob = context.active_object
7254 layout = self.layout
7256 box.prop( ob,
'cast_shadows' )
7258 box.prop( ob,
'use_draw_distance' )
7259 if ob.use_draw_distance:
7260 box.prop( ob,
'draw_distance' )
7262 if ob.type ==
'EMPTY':
7263 box.prop( ob,
'use_avatar' )
7264 box.prop( ob,
'avatar_reference' )
7267 class PANEL_Speaker(bpy.types.Panel):
7268 bl_space_type =
'PROPERTIES' 7269 bl_region_type =
'WINDOW' 7273 def poll(cls, context):
7274 if context.active_object
and context.active_object.type==
'SPEAKER':
return True 7275 def draw(self, context):
7276 layout = self.layout
7278 box.prop( context.active_object.data,
'play_on_load' )
7279 box.prop( context.active_object.data,
'loop' )
7280 box.prop( context.active_object.data,
'use_spatial' )
7283 class PANEL_MultiResLOD(bpy.types.Panel):
7284 bl_space_type =
'PROPERTIES' 7285 bl_region_type =
'WINDOW' 7286 bl_context =
"modifier" 7287 bl_label =
"Multi-Resolution LOD" 7289 def poll(cls, context):
7290 if context.active_object
and context.active_object.type==
'MESH':
7291 ob = context.active_object
7292 if ob.modifiers
and ob.modifiers[0].type==
'MULTIRES':
7294 def draw(self, context):
7295 ob = context.active_object
7296 layout = self.layout
7298 box.prop( ob,
'use_multires_lod' )
7299 if ob.use_multires_lod:
7300 box.prop( ob,
'multires_lod_range' )
7304 def material_name( mat, clean = False ):
7305 if type(mat)
is str:
7307 elif not mat.library:
7311 return clean_object_name(mat.name + mat.library.filepath.replace(
'/',
'_'))
7313 def export_mesh(ob, path='/tmp', force_name=None, ignore_shape_animation=False, normals=True):
7314 ''' returns materials used by the mesh ''' 7315 return dot_mesh( ob, path, force_name, ignore_shape_animation, normals )
7317 def generate_material(mat, path='/tmp', copy_programs=False, touch_textures=False):
7318 ''' returns generated material string ''' 7320 safename = material_name(mat)
7321 M =
'// %s generated by blender2ogre %s\n\n' % (mat.name, VERSION)
7323 M +=
'material %s \n{\n' % safename
7325 M += indent(1,
'receive_shadows on \n')
7327 M += indent(1,
'receive_shadows off \n')
7329 M += indent(1,
'technique',
'{' )
7330 w = OgreMaterialGenerator(mat, path=path, touch_textures=touch_textures)
7333 progs = w.get_active_programs()
7338 print(
'[WARNING}: material %s uses program %s which has no source' % (mat.name, prog.name) )
7340 header = w.get_header()
7341 passes = w.get_passes()
7343 M +=
'\n'.join(passes)
7344 M += indent(1,
'}' )
7348 return header +
'\n' + M
7352 def get_ogre_user_material( name ):
7353 if name
in MaterialScripts.ALL_MATERIALS:
7354 return MaterialScripts.ALL_MATERIALS[ name ]
7356 def get_shader_program( name ):
7357 if name
in OgreProgram.PROGRAMS:
7358 return OgreProgram.PROGRAMS[ name ]
7360 print(
'WARNING: no shader program named: %s' %name)
7362 def get_shader_programs():
7363 return OgreProgram.PROGRAMS.values()
7365 def parse_material_and_program_scripts( path, scripts, progs, missing ):
7366 for name
in os.listdir(path):
7367 url = os.path.join(path,name)
7368 if os.path.isdir( url ):
7369 parse_material_and_program_scripts( url, scripts, progs, missing )
7371 elif os.path.isfile( url ):
7372 if name.endswith(
'.material' ):
7373 print(
'<found material>', url )
7374 scripts.append( MaterialScripts( url ) )
7376 if name.endswith(
'.program'):
7377 print(
'<found program>', url )
7378 data = open( url,
'rb' ).read().decode(
'utf-8')
7380 chk = []; chunks = [ chk ]
7381 for line
in data.splitlines():
7382 line = line.split(
'//')[0]
7383 if line.startswith(
'}'):
7385 chk = []; chunks.append( chk )
7390 if not chk:
continue 7391 p = OgreProgram( data=
'\n'.join(chk) )
7394 if not ok: missing.append( p )
7395 else: progs.append( p )
7397 def update_parent_material_path( path ):
7399 print(
'>>SEARCHING FOR OGRE MATERIALS: %s' %path )
7403 parse_material_and_program_scripts( path, scripts, progs, missing )
7406 print(
'WARNING: missing shader programs:')
7407 for p
in missing: print(p.name)
7408 if missing
and not progs:
7409 print(
'WARNING: no shader programs were found - set "SHADER_PROGRAMS" to your path')
7411 MaterialScripts.reset_rna( callback=bpyShaders.on_change_parent_material )
7412 return scripts, progs
7414 def get_subcollision_meshes():
7415 ''' returns all collision meshes found in the scene ''' 7417 for ob
in bpy.context.scene.objects:
7418 if ob.type==
'MESH' and ob.subcollision: r.append( ob )
7421 def get_objects_with_subcollision():
7422 ''' returns objects that have active sub-collisions ''' 7424 for ob
in bpy.context.scene.objects:
7425 if ob.type==
'MESH' and ob.collision_mode
not in (
'NONE',
'PRIMITIVE'):
7429 def get_subcollisions(ob):
7430 prefix =
'%s.' %ob.collision_mode
7432 for child
in ob.children:
7433 if child.subcollision
and child.name.startswith( prefix ):
7437 class bpyShaders(bpy.types.Operator):
7438 '''operator: enables material nodes (workaround for not having IDPointers in pyRNA)''' 7439 bl_idname =
"ogre.force_setup_material_passes" 7440 bl_label =
"force bpyShaders" 7441 bl_options = {
'REGISTER'}
7444 def poll(cls, context):
7445 if context.active_object
and context.active_object.active_material:
return True 7446 def invoke(self, context, event):
7447 mat = context.active_object.active_material
7448 mat.use_material_passes =
True 7449 bpyShaders.create_material_passes( mat )
7454 def on_change_parent_material(mat,context):
7456 print(
'callback', mat.ogre_parent_material)
7459 def get_subnodes(mat, type='TEXTURE'):
7461 for node
in mat.nodes:
7462 if node.type==type: d[node.name] = node
7463 keys = list(d.keys())
7466 for key
in keys: r.append( d[key] )
7471 def get_texture_subnodes( parent, submaterial=None ):
7472 if not submaterial: submaterial = parent.active_node_material
7474 for link
in parent.node_tree.links:
7475 if link.from_node
and link.from_node.type==
'TEXTURE':
7476 if link.to_node
and link.to_node.type ==
'MATERIAL_EXT':
7477 if link.to_node.material:
7478 if link.to_node.material.name == submaterial.name:
7479 node = link.from_node
7481 keys = list(d.keys())
7484 for key
in keys: r.append( d[key] )
7488 def get_connected_input_nodes( material, node ):
7490 for link
in material.node_tree.links:
7491 if link.to_node
and link.to_node.name == node.name:
7492 r.append( link.from_node )
7496 def get_or_create_material_passes( mat, n=8 ):
7497 if not mat.node_tree:
7498 print(
'CREATING MATERIAL PASSES', n)
7499 bpyShaders.create_material_passes( mat, n )
7502 for node
in mat.node_tree.nodes:
7503 if node.type ==
'MATERIAL_EXT' and node.name.startswith(
'GEN.'):
7505 keys = list(d.keys())
7508 for key
in keys: r.append( d[key] )
7512 def get_or_create_texture_nodes( mat, n=6 ):
7514 assert mat.node_tree
7516 for node
in mat.node_tree.nodes:
7517 if node.type ==
'MATERIAL_EXT' and node.name.startswith(
'GEN.'):
7520 m = bpyShaders.get_or_create_material_passes(mat)
7523 for link
in mat.node_tree.links:
7524 print(link, link.to_node, link.from_node)
7525 if link.to_node
and link.to_node.name.startswith(
'GEN.')
and link.from_node.type==
'TEXTURE':
7526 r.append( link.from_node )
7528 print(
'--missing texture nodes--')
7529 r = bpyShaders.create_texture_nodes( mat, n )
7533 def create_material_passes( mat, n=8, textures=True ):
7535 mat.use_nodes =
True 7536 tree = mat.node_tree
7538 nodes = bpyShaders.get_subnodes( tree,
'MATERIAL' )
7539 if nodes
and not nodes[0].material:
7540 nodes[0].material = mat
7544 for i
in range( n ):
7545 node = tree.nodes.new( type=
'MATERIAL_EXT' )
7546 node.name =
'GEN.%s' %i
7547 node.location.x = x; node.location.y = 640
7552 texnodes = bpyShaders.create_texture_nodes( mat )
7557 def create_texture_nodes( mat, n=6, geoms=True ):
7559 assert mat.node_tree
7560 mats = bpyShaders.get_or_create_material_passes( mat )
7562 for i,m
in enumerate(mats):
7563 r[
'material'] = m; r[
'textures'] = []; r[
'geoms'] = []
7565 for tag
in [
'Mirror',
'Ambient',
'Emit',
'SpecTra',
'Ray Mirror',
'Translucency']:
7566 inputs.append( m.inputs[ tag ] )
7568 tex = mat.node_tree.nodes.new( type=
'TEXTURE' )
7569 tex.name =
'TEX.%s.%s' %(j, m.name)
7570 tex.location.x = x - (j*16)
7571 tex.location.y = -(j*230)
7572 input = inputs[j]; output = tex.outputs[
'Color']
7573 link = mat.node_tree.links.new( input, output )
7574 r[
'textures'].append( tex )
7576 geo = mat.node_tree.nodes.new( type=
'GEOMETRY' )
7577 link = mat.node_tree.links.new( tex.inputs[
'Vector'], geo.outputs[
'UV'] )
7578 geo.location.x = x - (j*16) - 250
7579 geo.location.y = -(j*250) - 1500
7580 r[
'geoms'].append( geo )
7585 class PANEL_node_editor_ui( bpy.types.Panel ):
7586 bl_space_type =
'NODE_EDITOR' 7587 bl_region_type =
'UI' 7588 bl_label =
"Ogre Material" 7591 def poll(self,context):
7592 if context.space_data.id:
7595 def draw(self, context):
7596 layout = self.layout
7597 topmat = context.space_data.id
7598 mat = topmat.active_node_material
7599 if not mat
or topmat.name == mat.name:
7600 self.bl_label = topmat.name
7601 if not topmat.use_material_passes:
7603 'ogre.force_setup_material_passes',
7604 text=
"Ogre Material Layers",
7607 ogre_material_panel( layout, topmat, show_programs=
False )
7609 self.bl_label = mat.name
7610 ogre_material_panel( layout, mat, topmat, show_programs=
False )
7613 class PANEL_node_editor_ui_extra( bpy.types.Panel ):
7614 bl_space_type =
'NODE_EDITOR' 7615 bl_region_type =
'UI' 7616 bl_label =
"Ogre Material Advanced" 7617 bl_options = {
'DEFAULT_CLOSED'}
7619 def poll(self,context):
7620 if context.space_data.id:
return True 7621 def draw(self, context):
7622 layout = self.layout
7623 topmat = context.space_data.id
7624 mat = topmat.active_node_material
7626 self.bl_label = mat.name +
' (advanced)' 7627 ogre_material_panel_extra( layout, mat )
7629 self.bl_label = topmat.name +
' (advanced)' 7630 ogre_material_panel_extra( layout, topmat )
7632 def ogre_material_panel_extra( parent, mat ):
7636 if mat.use_fixed_pipeline:
7637 header.prop( mat,
'use_fixed_pipeline', text=
'Fixed Pipeline', icon=
'LAMP_SUN' )
7639 row.prop(mat,
"use_vertex_color_paint", text=
"Vertex Colors")
7640 row.prop(mat,
"use_shadeless")
7641 if mat.use_shadeless
and not mat.use_vertex_color_paint:
7643 row.prop(mat,
"diffuse_color", text=
'')
7644 elif not mat.use_shadeless:
7645 if not mat.use_vertex_color_paint:
7647 row.prop(mat,
"diffuse_color", text=
'')
7648 row.prop(mat,
"diffuse_intensity", text=
'intensity')
7650 row.prop(mat,
"specular_color", text=
'')
7651 row.prop(mat,
"specular_intensity", text=
'intensity')
7653 row.prop(mat,
"specular_hardness")
7655 row.prop(mat,
"ambient")
7657 row.prop(mat,
"emit")
7658 box.prop(mat,
'use_ogre_advanced_options', text=
'---guru options---' )
7660 header.prop( mat,
'use_fixed_pipeline', text=
'', icon=
'LAMP_SUN' )
7661 header.prop(mat,
'use_ogre_advanced_options', text=
'---guru options---' )
7663 if mat.use_ogre_advanced_options:
7664 box.prop(mat,
'offset_z')
7665 box.prop(mat,
"use_shadows")
7666 box.prop(mat,
'ogre_depth_write' )
7667 for tag
in 'ogre_colour_write ogre_lighting ogre_normalise_normals ogre_light_clip_planes ogre_light_scissor ogre_alpha_to_coverage ogre_depth_check'.split():
7669 for tag
in 'ogre_polygon_mode ogre_shading ogre_cull_hardware ogre_transparent_sorting ogre_illumination_stage ogre_depth_func ogre_scene_blend_op'.split():
7672 def ogre_material_panel( layout, mat, parent=None, show_programs=True ):
7675 header.prop(mat,
'ogre_scene_blend', text=
'')
7676 if mat.ogre_scene_blend
and 'alpha' in mat.ogre_scene_blend:
7678 if mat.use_transparency:
7679 row.prop(mat,
"use_transparency", text=
'')
7680 row.prop(mat,
"alpha")
7682 row.prop(mat,
"use_transparency", text=
'Transparent')
7686 header.prop(mat,
'use_ogre_parent_material', icon=
'FILE_SCRIPT', text=
'')
7688 if mat.use_ogre_parent_material:
7690 row.prop(mat,
'ogre_parent_material', text=
'')
7692 s = get_ogre_user_material( mat.ogre_parent_material )
7693 if s
and (s.vertex_programs
or s.fragment_programs):
7694 progs = s.get_programs()
7699 texnodes = bpyShaders.get_texture_subnodes( parent, submaterial=mat )
7701 texnodes = bpyShaders.get_texture_subnodes( mat )
7705 bx.label( text=
'(missing shader programs)', icon=
'ERROR' )
7706 elif s.texture_units
and texnodes:
7708 for i,name
in enumerate(s.texture_units_order):
7713 row.prop( tex,
'texture', text=name )
7715 inputs = bpyShaders.get_connected_input_nodes( parent, tex )
7718 assert geo.type ==
'GEOMETRY' 7719 row.prop( geo,
'uv_layer', text=
'UV' )
7721 print(
'WARNING: no slot for texture unit:', name)
7723 if show_programs
and (s.vertex_programs
or s.fragment_programs):
7725 for name
in s.vertex_programs:
7726 bx.label( text=name )
7727 for name
in s.fragment_programs:
7728 bx.label( text=name )
7733 if __name__ ==
"__main__":
def save(blenderobject, path)
def dotmesh(path, facesAddr, facesSmoothAddr, facesMatAddr, vertsPosAddr, vertsNorAddr, numFaces, numVerts, materialNames)
def hide_user_interface()