1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
21 # Contributors: bart:neeneenee*de, http://www.neeneenee.de/vrml, Campbell Barton
24 This script exports to X3D format.
27 Run this script from "File->Export" menu. A pop-up will ask whether you
28 want to export only selected or all relevant objects.
31 Doesn't handle multiple materials (don't use material indices);<br>
32 Doesn't handle multiple UV textures on a single mesh (create a mesh for each texture);<br>
33 Can't get the texture array associated with material * not the UV ones;
42 from bpy_extras
.io_utils
import create_derived_objects
, free_derived_objects
46 H3D_TOP_LEVEL
= 'TOP_LEVEL_TI'
47 H3D_CAMERA_FOLLOW
= 'CAMERA_FOLLOW_TRANSFORM'
48 H3D_VIEW_MATRIX
= 'view_matrix'
52 return tuple([max(min(c
, 1.0), 0.0) for c
in col
])
55 def matrix_direction_neg_z(matrix
):
56 return (matrix
.to_3x3() * mathutils
.Vector((0.0, 0.0, -1.0))).normalized()[:]
59 def prefix_quoted_str(value
, prefix
):
60 return value
[0] + prefix
+ value
[1:]
63 def suffix_quoted_str(value
, suffix
):
64 return value
[:-1] + suffix
+ value
[-1:]
67 def bool_as_str(value
):
68 return ('false', 'true')[bool(value
)]
76 if txt
[0] in "1234567890+-":
78 return txt
.translate({
79 # control characters 0x0-0x1f
129 def build_hierarchy(objects
):
130 """ returns parent child relationships, skipping
132 objects_set
= set(objects
)
135 def test_parent(parent
):
136 while (parent
is not None) and (parent
not in objects_set
):
137 parent
= parent
.parent
141 par_lookup
.setdefault(test_parent(obj
.parent
), []).append((obj
, []))
143 for parent
, children
in par_lookup
.items():
144 for obj
, subchildren
in children
:
145 subchildren
[:] = par_lookup
.get(obj
, [])
147 return par_lookup
.get(None, [])
150 # -----------------------------------------------------------------------------
152 # -----------------------------------------------------------------------------
153 def h3d_shader_glsl_frag_patch(filepath
, scene
, global_vars
, frag_uniform_var_map
):
154 h3d_file
= open(filepath
, 'r', encoding
='utf-8')
157 last_transform
= None
160 if l
.startswith("void main(void)"):
162 lines
.append("// h3d custom vars begin\n")
163 for v
in global_vars
:
164 lines
.append("%s\n" % v
)
165 lines
.append("// h3d custom vars end\n")
167 elif l
.lstrip().startswith("lamp_visibility_other("):
169 last_transform
= w
[1] + "_transform" # XXX - HACK!!!
170 w
[1] = '(view_matrix * %s_transform * vec4(%s.x, %s.y, %s.z, 1.0)).xyz' % (w
[1], w
[1], w
[1], w
[1])
172 elif l
.lstrip().startswith("lamp_visibility_sun_hemi("):
174 w
[0] = w
[0][len("lamp_visibility_sun_hemi(") + 1:]
176 if not h3d_is_object_view(scene
, frag_uniform_var_map
[w
[0]]):
177 w
[0] = '(mat3(normalize(view_matrix[0].xyz), normalize(view_matrix[1].xyz), normalize(view_matrix[2].xyz)) * -%s)' % w
[0]
179 w
[0] = ('(mat3(normalize((view_matrix*%s)[0].xyz), normalize((view_matrix*%s)[1].xyz), normalize((view_matrix*%s)[2].xyz)) * -%s)' %
180 (last_transform
, last_transform
, last_transform
, w
[0]))
182 l
= "\tlamp_visibility_sun_hemi(" + ", ".join(w
)
183 elif l
.lstrip().startswith("lamp_visibility_spot_circle("):
185 w
[0] = w
[0][len("lamp_visibility_spot_circle(") + 1:]
187 if not h3d_is_object_view(scene
, frag_uniform_var_map
[w
[0]]):
188 w
[0] = '(mat3(normalize(view_matrix[0].xyz), normalize(view_matrix[1].xyz), normalize(view_matrix[2].xyz)) * -%s)' % w
[0]
190 w
[0] = ('(mat3(normalize((view_matrix*%s)[0].xyz), normalize((view_matrix*%s)[1].xyz), normalize((view_matrix*%s)[2].xyz)) * %s)' %
191 (last_transform
, last_transform
, last_transform
, w
[0]))
193 l
= "\tlamp_visibility_spot_circle(" + ", ".join(w
)
199 h3d_file
= open(filepath
, 'w', encoding
='utf-8')
200 h3d_file
.writelines(lines
)
204 def h3d_is_object_view(scene
, obj
):
205 camera
= scene
.camera
210 parent
= parent
.parent
214 # -----------------------------------------------------------------------------
215 # Functions for writing output file
216 # -----------------------------------------------------------------------------
221 use_mesh_modifiers
=False,
223 use_triangulate
=False,
228 name_decorations
=True,
231 # -------------------------------------------------------------------------
233 # -------------------------------------------------------------------------
235 from bpy_extras
.io_utils
import unique_name
236 from xml
.sax
.saxutils
import quoteattr
, escape
239 # If names are decorated, the uuid map can be split up
240 # by type for efficiency of collision testing
241 # since objects of different types will always have
242 # different decorated names.
243 uuid_cache_object
= {} # object
244 uuid_cache_lamp
= {} # 'LA_' + object.name
245 uuid_cache_view
= {} # object, different namespace
246 uuid_cache_mesh
= {} # mesh
247 uuid_cache_material
= {} # material
248 uuid_cache_image
= {} # image
249 uuid_cache_world
= {} # world
259 # If names are not decorated, it may be possible for two objects to
260 # have the same name, so there has to be a unified dictionary to
261 # prevent uuid collisions.
263 uuid_cache_object
= uuid_cache
# object
264 uuid_cache_lamp
= uuid_cache
# 'LA_' + object.name
265 uuid_cache_view
= uuid_cache
# object, different namespace
266 uuid_cache_mesh
= uuid_cache
# mesh
267 uuid_cache_material
= uuid_cache
# material
268 uuid_cache_image
= uuid_cache
# image
269 uuid_cache_world
= uuid_cache
# world
280 _TRANSFORM
= '_TRANSFORM'
282 # store files to copy
285 # store names of newly cerated meshes, so we dont overlap
286 mesh_name_set
= set()
289 base_src
= os
.path
.dirname(bpy
.data
.filepath
)
290 base_dst
= os
.path
.dirname(file.name
)
291 filename_strip
= os
.path
.splitext(os
.path
.basename(file.name
))[0]
292 gpu_shader_cache
= {}
296 gpu_shader_dummy_mat
= bpy
.data
.materials
.new('X3D_DYMMY_MAT')
297 gpu_shader_cache
[None] = gpu
.export_shader(scene
, gpu_shader_dummy_mat
)
298 h3d_material_route
= []
300 # -------------------------------------------------------------------------
301 # File Writing Functions
302 # -------------------------------------------------------------------------
304 def writeHeader(ident
):
305 filepath_quoted
= quoteattr(os
.path
.basename(file.name
))
306 blender_ver_quoted
= quoteattr('Blender %s' % bpy
.app
.version_string
)
308 fw('%s<?xml version="1.0" encoding="UTF-8"?>\n' % ident
)
310 fw('%s<X3D profile="H3DAPI" version="1.4">\n' % ident
)
312 fw('%s<!DOCTYPE X3D PUBLIC "ISO//Web3D//DTD X3D 3.0//EN" "http://www.web3d.org/specifications/x3d-3.0.dtd">\n' % ident
)
313 fw('%s<X3D version="3.0" profile="Immersive" xmlns:xsd="http://www.w3.org/2001/XMLSchema-instance" xsd:noNamespaceSchemaLocation="http://www.web3d.org/specifications/x3d-3.0.xsd">\n' % ident
)
316 fw('%s<head>\n' % ident
)
318 fw('%s<meta name="filename" content=%s />\n' % (ident
, filepath_quoted
))
319 fw('%s<meta name="generator" content=%s />\n' % (ident
, blender_ver_quoted
))
320 # this info was never updated, so blender version should be enough
321 # fw('%s<meta name="translator" content="X3D exporter v1.55 (2006/01/17)" />\n' % ident)
323 fw('%s</head>\n' % ident
)
324 fw('%s<Scene>\n' % ident
)
328 # outputs the view matrix in glModelViewMatrix field
329 fw('%s<TransformInfo DEF="%s" outputGLMatrices="true" />\n' % (ident
, H3D_TOP_LEVEL
))
333 def writeFooter(ident
):
337 for route
in h3d_material_route
:
338 fw('%s%s\n' % (ident
, route
))
341 fw('%s</Scene>\n' % ident
)
343 fw('%s</X3D>' % ident
)
346 def writeViewpoint(ident
, obj
, matrix
, scene
):
347 view_id
= quoteattr(unique_name(obj
, CA_
+ obj
.name
, uuid_cache_view
, clean_func
=clean_def
, sep
="_"))
349 loc
, rot
, scale
= matrix
.decompose()
350 rot
= rot
.to_axis_angle()
351 rot
= (*rot
[0].normalized(), rot
[1])
353 ident_step
= ident
+ (' ' * (-len(ident
) + \
354 fw('%s<Viewpoint ' % ident
)))
355 fw('DEF=%s\n' % view_id
)
356 fw(ident_step
+ 'centerOfRotation="0 0 0"\n')
357 fw(ident_step
+ 'position="%3.2f %3.2f %3.2f"\n' % loc
[:])
358 fw(ident_step
+ 'orientation="%3.2f %3.2f %3.2f %3.2f"\n' % rot
)
359 fw(ident_step
+ 'fieldOfView="%.3f"\n' % obj
.data
.angle
)
360 fw(ident_step
+ '/>\n')
362 def writeFog(ident
, world
):
364 mtype
= world
.mist_settings
.falloff
365 mparam
= world
.mist_settings
370 ident_step
= ident
+ (' ' * (-len(ident
) + \
371 fw('%s<Fog ' % ident
)))
372 fw('fogType="%s"\n' % ('LINEAR' if (mtype
== 'LINEAR') else 'EXPONENTIAL'))
373 fw(ident_step
+ 'color="%.3f %.3f %.3f"\n' % clamp_color(world
.horizon_color
))
374 fw(ident_step
+ 'visibilityRange="%.3f"\n' % mparam
.depth
)
375 fw(ident_step
+ '/>\n')
379 def writeNavigationInfo(ident
, scene
, has_lamp
):
380 ident_step
= ident
+ (' ' * (-len(ident
) + \
381 fw('%s<NavigationInfo ' % ident
)))
382 fw('headlight="%s"\n' % bool_as_str(not has_lamp
))
383 fw(ident_step
+ 'visibilityLimit="0.0"\n')
384 fw(ident_step
+ 'type=\'"EXAMINE", "ANY"\'\n')
385 fw(ident_step
+ 'avatarSize="0.25, 1.75, 0.75"\n')
386 fw(ident_step
+ '/>\n')
388 def writeTransform_begin(ident
, matrix
, def_id
):
389 ident_step
= ident
+ (' ' * (-len(ident
) + \
390 fw('%s<Transform ' % ident
)))
391 if def_id
is not None:
392 fw('DEF=%s\n' % def_id
)
396 loc
, rot
, sca
= matrix
.decompose()
397 rot
= rot
.to_axis_angle()
398 rot
= (*rot
[0], rot
[1])
400 fw(ident_step
+ 'translation="%.6f %.6f %.6f"\n' % loc
[:])
401 # fw(ident_step + 'center="%.6f %.6f %.6f"\n' % (0, 0, 0))
402 fw(ident_step
+ 'scale="%.6f %.6f %.6f"\n' % sca
[:])
403 fw(ident_step
+ 'rotation="%.6f %.6f %.6f %.6f"\n' % rot
)
404 fw(ident_step
+ '>\n')
408 def writeTransform_end(ident
):
410 fw('%s</Transform>\n' % ident
)
413 def writeSpotLight(ident
, obj
, matrix
, lamp
, world
):
414 # note, lamp_id is not re-used
415 lamp_id
= quoteattr(unique_name(obj
, LA_
+ obj
.name
, uuid_cache_lamp
, clean_func
=clean_def
, sep
="_"))
418 ambi
= world
.ambient_color
419 amb_intensity
= ((ambi
[0] + ambi
[1] + ambi
[2]) / 3.0) / 2.5
424 # compute cutoff and beamwidth
425 intensity
= min(lamp
.energy
/ 1.75, 1.0)
426 beamWidth
= lamp
.spot_size
* 0.37
427 # beamWidth=((lamp.spotSize*math.pi)/180.0)*.37
428 cutOffAngle
= beamWidth
* 1.3
430 orientation
= matrix_direction_neg_z(matrix
)
432 location
= matrix
.to_translation()[:]
434 radius
= lamp
.distance
* math
.cos(beamWidth
)
435 # radius = lamp.dist*math.cos(beamWidth)
436 ident_step
= ident
+ (' ' * (-len(ident
) + \
437 fw('%s<SpotLight ' % ident
)))
438 fw('DEF=%s\n' % lamp_id
)
439 fw(ident_step
+ 'radius="%.4f"\n' % radius
)
440 fw(ident_step
+ 'ambientIntensity="%.4f"\n' % amb_intensity
)
441 fw(ident_step
+ 'intensity="%.4f"\n' % intensity
)
442 fw(ident_step
+ 'color="%.4f %.4f %.4f"\n' % clamp_color(lamp
.color
))
443 fw(ident_step
+ 'beamWidth="%.4f"\n' % beamWidth
)
444 fw(ident_step
+ 'cutOffAngle="%.4f"\n' % cutOffAngle
)
445 fw(ident_step
+ 'direction="%.4f %.4f %.4f"\n' % orientation
)
446 fw(ident_step
+ 'location="%.4f %.4f %.4f"\n' % location
)
447 fw(ident_step
+ '/>\n')
449 def writeDirectionalLight(ident
, obj
, matrix
, lamp
, world
):
450 # note, lamp_id is not re-used
451 lamp_id
= quoteattr(unique_name(obj
, LA_
+ obj
.name
, uuid_cache_lamp
, clean_func
=clean_def
, sep
="_"))
454 ambi
= world
.ambient_color
456 amb_intensity
= ((float(ambi
[0] + ambi
[1] + ambi
[2])) / 3.0) / 2.5
461 intensity
= min(lamp
.energy
/ 1.75, 1.0)
463 orientation
= matrix_direction_neg_z(matrix
)
465 ident_step
= ident
+ (' ' * (-len(ident
) + \
466 fw('%s<DirectionalLight ' % ident
)))
467 fw('DEF=%s\n' % lamp_id
)
468 fw(ident_step
+ 'ambientIntensity="%.4f"\n' % amb_intensity
)
469 fw(ident_step
+ 'color="%.4f %.4f %.4f"\n' % clamp_color(lamp
.color
))
470 fw(ident_step
+ 'intensity="%.4f"\n' % intensity
)
471 fw(ident_step
+ 'direction="%.4f %.4f %.4f"\n' % orientation
)
472 fw(ident_step
+ '/>\n')
474 def writePointLight(ident
, obj
, matrix
, lamp
, world
):
475 # note, lamp_id is not re-used
476 lamp_id
= quoteattr(unique_name(obj
, LA_
+ obj
.name
, uuid_cache_lamp
, clean_func
=clean_def
, sep
="_"))
479 ambi
= world
.ambient_color
481 amb_intensity
= ((float(ambi
[0] + ambi
[1] + ambi
[2])) / 3.0) / 2.5
486 intensity
= min(lamp
.energy
/ 1.75, 1.0)
487 location
= matrix
.to_translation()[:]
489 ident_step
= ident
+ (' ' * (-len(ident
) + \
490 fw('%s<PointLight ' % ident
)))
491 fw('DEF=%s\n' % lamp_id
)
492 fw(ident_step
+ 'ambientIntensity="%.4f"\n' % amb_intensity
)
493 fw(ident_step
+ 'color="%.4f %.4f %.4f"\n' % clamp_color(lamp
.color
))
495 fw(ident_step
+ 'intensity="%.4f"\n' % intensity
)
496 fw(ident_step
+ 'radius="%.4f" \n' % lamp
.distance
)
497 fw(ident_step
+ 'location="%.4f %.4f %.4f"\n' % location
)
498 fw(ident_step
+ '/>\n')
500 def writeIndexedFaceSet(ident
, obj
, mesh
, matrix
, world
):
501 obj_id
= quoteattr(unique_name(obj
, OB_
+ obj
.name
, uuid_cache_object
, clean_func
=clean_def
, sep
="_"))
502 mesh_id
= quoteattr(unique_name(mesh
, ME_
+ mesh
.name
, uuid_cache_mesh
, clean_func
=clean_def
, sep
="_"))
503 mesh_id_group
= prefix_quoted_str(mesh_id
, group_
)
504 mesh_id_coords
= prefix_quoted_str(mesh_id
, 'coords_')
505 mesh_id_normals
= prefix_quoted_str(mesh_id
, 'normals_')
507 # tessellation faces may not exist
508 if not mesh
.tessfaces
and mesh
.polygons
:
509 mesh
.update(calc_tessface
=True)
511 if not mesh
.tessfaces
:
514 use_collnode
= bool([mod
for mod
in obj
.modifiers
515 if mod
.type == 'COLLISION'
516 if mod
.show_viewport
])
519 fw('%s<Collision enabled="true">\n' % ident
)
522 # use _ifs_TRANSFORM suffix so we dont collide with transform node when
523 # hierarchys are used.
524 ident
= writeTransform_begin(ident
, matrix
, suffix_quoted_str(obj_id
, "_ifs" + _TRANSFORM
))
527 fw('%s<Group USE=%s />\n' % (ident
, mesh_id_group
))
531 fw('%s<Group DEF=%s>\n' % (ident
, mesh_id_group
))
534 is_uv
= bool(mesh
.tessface_uv_textures
.active
)
535 # is_col, defined for each material
537 is_coords_written
= False
539 mesh_materials
= mesh
.materials
[:]
540 if not mesh_materials
:
541 mesh_materials
= [None]
543 mesh_material_tex
= [None] * len(mesh_materials
)
544 mesh_material_mtex
= [None] * len(mesh_materials
)
545 mesh_material_images
= [None] * len(mesh_materials
)
547 for i
, material
in enumerate(mesh_materials
):
549 for mtex
in material
.texture_slots
:
552 if tex
and tex
.type == 'IMAGE':
555 mesh_material_tex
[i
] = tex
556 mesh_material_mtex
[i
] = mtex
557 mesh_material_images
[i
] = image
560 mesh_materials_use_face_texture
= [getattr(material
, 'use_face_texture', True) for material
in mesh_materials
]
563 mesh_vertices
= mesh
.vertices
[:]
564 mesh_faces
= mesh
.tessfaces
[:]
565 mesh_faces_materials
= [f
.material_index
for f
in mesh_faces
]
566 mesh_faces_vertices
= [f
.vertices
[:] for f
in mesh_faces
]
568 if is_uv
and True in mesh_materials_use_face_texture
:
569 mesh_faces_image
= [(fuv
.image
570 if mesh_materials_use_face_texture
[mesh_faces_materials
[i
]]
571 else mesh_material_images
[mesh_faces_materials
[i
]])
572 for i
, fuv
in enumerate(mesh
.tessface_uv_textures
.active
.data
)]
574 mesh_faces_image_unique
= set(mesh_faces_image
)
575 elif len(set(mesh_material_images
) | {None}
) > 1: # make sure there is at least one image
576 mesh_faces_image
= [mesh_material_images
[material_index
] for material_index
in mesh_faces_materials
]
577 mesh_faces_image_unique
= set(mesh_faces_image
)
579 mesh_faces_image
= [None] * len(mesh_faces
)
580 mesh_faces_image_unique
= {None}
584 for material_index
in range(len(mesh_materials
)):
585 for image
in mesh_faces_image_unique
:
586 face_groups
[material_index
, image
] = []
587 del mesh_faces_image_unique
589 for i
, (material_index
, image
) in enumerate(zip(mesh_faces_materials
, mesh_faces_image
)):
590 face_groups
[material_index
, image
].append(i
)
592 # same as face_groups.items() but sorted so we can get predictable output.
593 face_groups_items
= list(face_groups
.items())
594 face_groups_items
.sort(key
=lambda m
: (m
[0][0], getattr(m
[0][1], 'name', '')))
596 for (material_index
, image
), face_group
in face_groups_items
: # face_groups.items()
598 material
= mesh_materials
[material_index
]
600 fw('%s<Shape>\n' % ident
)
604 is_col
= (mesh
.tessface_vertex_colors
.active
and (material
is None or material
.use_vertex_color_paint
))
606 # kludge but as good as it gets!
608 if mesh_faces
[i
].use_smooth
:
612 # UV's and VCols split verts off which effects smoothing
613 # force writing normals in this case.
614 # Also, creaseAngle is not supported for IndexedTriangleSet,
615 # so write normals when is_smooth (otherwise
616 # IndexedTriangleSet can have only all smooth/all flat shading).
617 is_force_normals
= use_triangulate
and (is_smooth
or is_uv
or is_col
)
620 gpu_shader
= gpu_shader_cache
.get(material
) # material can be 'None', uses dummy cache
621 if gpu_shader
is None:
622 gpu_shader
= gpu_shader_cache
[material
] = gpu
.export_shader(scene
, material
)
625 gpu_shader_tmp
= gpu
.export_shader(scene
, material
)
627 print('\nWRITING MATERIAL:', material
.name
)
628 del gpu_shader_tmp
['fragment']
629 del gpu_shader_tmp
['vertex']
630 pprint
.pprint(gpu_shader_tmp
, width
=120)
631 #pprint.pprint(val['vertex'])
634 fw('%s<Appearance>\n' % ident
)
637 if image
and not use_h3d
:
638 writeImageTexture(ident
, image
)
640 if mesh_materials_use_face_texture
[material_index
]:
642 fw('%s<TextureTransform scale="%s %s" />\n' % (ident
, image
.tiles_x
, image
.tiles_y
))
645 loc
= mesh_material_mtex
[material_index
].offset
[:2]
647 # mtex_scale * tex_repeat
648 sca_x
, sca_y
= mesh_material_mtex
[material_index
].scale
[:2]
650 sca_x
*= mesh_material_tex
[material_index
].repeat_x
651 sca_y
*= mesh_material_tex
[material_index
].repeat_y
653 # flip x/y is a sampling feature, convert to transform
654 if mesh_material_tex
[material_index
].use_flip_axis
:
656 sca_x
, sca_y
= sca_y
, -sca_x
660 ident_step
= ident
+ (' ' * (-len(ident
) + \
661 fw('%s<TextureTransform ' % ident
)))
663 # fw('center="%.6f %.6f" ' % (0.0, 0.0))
664 fw(ident_step
+ 'translation="%.6f %.6f"\n' % loc
)
665 fw(ident_step
+ 'scale="%.6f %.6f"\n' % (sca_x
, sca_y
))
666 fw(ident_step
+ 'rotation="%.6f"\n' % rot
)
667 fw(ident_step
+ '/>\n')
670 mat_tmp
= material
if material
else gpu_shader_dummy_mat
671 writeMaterialH3D(ident
, mat_tmp
, world
,
676 writeMaterial(ident
, material
, world
)
679 fw('%s</Appearance>\n' % ident
)
681 mesh_faces_col
= mesh
.tessface_vertex_colors
.active
.data
if is_col
else None
682 mesh_faces_uv
= mesh
.tessface_uv_textures
.active
.data
if is_uv
else None
684 #-- IndexedFaceSet or IndexedLineSet
686 ident_step
= ident
+ (' ' * (-len(ident
) + \
687 fw('%s<IndexedTriangleSet ' % ident
)))
689 # --- Write IndexedTriangleSet Attributes (same as IndexedFaceSet)
690 fw('solid="%s"\n' % bool_as_str(material
and material
.game_settings
.use_backface_culling
))
692 if use_normals
or is_force_normals
:
693 fw(ident_step
+ 'normalPerVertex="true"\n')
695 # Tell X3D browser to generate flat (per-face) normals
696 fw(ident_step
+ 'normalPerVertex="false"\n')
705 def vertex_key(fidx
, f_cnr_idx
):
707 mesh_faces_uv
[fidx
].uv
[f_cnr_idx
][:],
708 getattr(mesh_faces_col
[fidx
], "color%d" % (f_cnr_idx
+ 1))[:],
713 def vertex_key(fidx
, f_cnr_idx
):
715 mesh_faces_uv
[fidx
].uv
[f_cnr_idx
][:],
720 def vertex_key(fidx
, f_cnr_idx
):
722 getattr(mesh_faces_col
[fidx
], "color%d" % (f_cnr_idx
+ 1))[:],
725 # ack, not especially efficient in this case
726 def vertex_key(fidx
, f_cnr_idx
):
729 # build a mesh mapping dict
730 vertex_hash
= [{} for i
in range(len(mesh
.vertices
))]
731 # worst case every face is a quad
732 face_tri_list
= [[None, None, None] for i
in range(len(mesh
.tessfaces
) * 2)]
736 temp_face
= [None] * 4
738 fv
= mesh_faces_vertices
[i
]
739 for j
, v_idx
in enumerate(fv
):
740 key
= vertex_key(i
, j
)
741 vh
= vertex_hash
[v_idx
]
744 x3d_v
= key
, v_idx
, totvert
746 # key / original_vertex / new_vertex
747 vert_tri_list
.append(x3d_v
)
752 f_iter
= ((0, 1, 2), (0, 2, 3))
754 f_iter
= ((0, 1, 2), )
757 # loop over a quad as 2 tris
758 f_tri
= face_tri_list
[totface
]
759 for ji
, j
in enumerate(f_it
):
760 f_tri
[ji
] = temp_face
[j
]
761 # quads run this twice
765 face_tri_list
[totface
:] = []
767 fw(ident_step
+ 'index="')
768 for x3d_f
in face_tri_list
:
769 fw('%i %i %i ' % (x3d_f
[0][2], x3d_f
[1][2], x3d_f
[2][2]))
772 # close IndexedTriangleSet
773 fw(ident_step
+ '>\n')
776 fw('%s<Coordinate ' % ident
)
778 for x3d_v
in vert_tri_list
:
779 fw('%.6f %.6f %.6f ' % mesh_vertices
[x3d_v
[1]].co
[:])
782 if use_normals
or is_force_normals
:
783 fw('%s<Normal ' % ident
)
785 for x3d_v
in vert_tri_list
:
786 fw('%.6f %.6f %.6f ' % mesh_vertices
[x3d_v
[1]].normal
[:])
790 fw('%s<TextureCoordinate point="' % ident
)
791 for x3d_v
in vert_tri_list
:
792 fw('%.4f %.4f ' % x3d_v
[0][slot_uv
])
796 fw('%s<Color color="' % ident
)
797 for x3d_v
in vert_tri_list
:
798 fw('%.3f %.3f %.3f ' % x3d_v
[0][slot_col
])
803 for gpu_attr
in gpu_shader
['attributes']:
806 if gpu_attr
['type'] == gpu
.CD_MTFACE
:
807 if gpu_attr
['datatype'] == gpu
.GPU_DATA_2F
:
808 fw('%s<FloatVertexAttribute ' % ident
)
809 fw('name="%s" ' % gpu_attr
['varname'])
810 fw('numComponents="2" ')
812 for x3d_v
in vert_tri_list
:
813 fw('%.4f %.4f ' % x3d_v
[0][slot_uv
])
818 elif gpu_attr
['type'] == gpu
.CD_MCOL
:
819 if gpu_attr
['datatype'] == gpu
.GPU_DATA_4UB
:
820 pass # XXX, H3D can't do
826 fw('%s</IndexedTriangleSet>\n' % ident
)
829 ident_step
= ident
+ (' ' * (-len(ident
) + \
830 fw('%s<IndexedFaceSet ' % ident
)))
832 # --- Write IndexedFaceSet Attributes (same as IndexedTriangleSet)
833 fw('solid="%s"\n' % bool_as_str(material
and material
.game_settings
.use_backface_culling
))
835 # use Auto-Smooth angle, if enabled. Otherwise make
836 # the mesh perfectly smooth by creaseAngle > pi.
837 fw(ident_step
+ 'creaseAngle="%.4f"\n' % (mesh
.auto_smooth_angle
if mesh
.use_auto_smooth
else 4.0))
840 # currently not optional, could be made so:
841 fw(ident_step
+ 'normalPerVertex="true"\n')
843 # IndexedTriangleSet assumes true
845 fw(ident_step
+ 'colorPerVertex="false"\n')
847 # for IndexedTriangleSet we use a uv per vertex so this isnt needed.
849 fw(ident_step
+ 'texCoordIndex="')
853 if len(mesh_faces_vertices
[i
]) == 4:
854 fw('%d %d %d %d -1 ' % (j
, j
+ 1, j
+ 2, j
+ 3))
857 fw('%d %d %d -1 ' % (j
, j
+ 1, j
+ 2))
860 # --- end texCoordIndex
863 fw(ident_step
+ 'coordIndex="')
865 fv
= mesh_faces_vertices
[i
]
867 fw('%i %i %i -1 ' % fv
)
869 fw('%i %i %i %i -1 ' % fv
)
874 # close IndexedFaceSet
875 fw(ident_step
+ '>\n')
878 # --- Write IndexedFaceSet Elements
880 if is_coords_written
:
881 fw('%s<Coordinate USE=%s />\n' % (ident
, mesh_id_coords
))
883 fw('%s<Normal USE=%s />\n' % (ident
, mesh_id_normals
))
885 ident_step
= ident
+ (' ' * (-len(ident
) + \
886 fw('%s<Coordinate ' % ident
)))
887 fw('DEF=%s\n' % mesh_id_coords
)
888 fw(ident_step
+ 'point="')
889 for v
in mesh
.vertices
:
890 fw('%.6f %.6f %.6f ' % v
.co
[:])
892 fw(ident_step
+ '/>\n')
894 is_coords_written
= True
897 ident_step
= ident
+ (' ' * (-len(ident
) + \
898 fw('%s<Normal ' % ident
)))
899 fw('DEF=%s\n' % mesh_id_normals
)
900 fw(ident_step
+ 'vector="')
901 for v
in mesh
.vertices
:
902 fw('%.6f %.6f %.6f ' % v
.normal
[:])
904 fw(ident_step
+ '/>\n')
907 fw('%s<TextureCoordinate point="' % ident
)
909 for uv
in mesh_faces_uv
[i
].uv
:
910 fw('%.4f %.4f ' % uv
[:])
915 fw('%s<Color color="' % ident
)
916 # XXX, 1 color per face, only
918 fw('%.3f %.3f %.3f ' % mesh_faces_col
[i
].color1
[:])
921 #--- output vertexColors
923 #--- output closing braces
926 fw('%s</IndexedFaceSet>\n' % ident
)
929 fw('%s</Shape>\n' % ident
)
933 #fw('%s<PythonScript DEF="PS" url="object.py" >\n' % ident)
934 #fw('%s <ShaderProgram USE="MA_Material.005" containerField="references"/>\n' % ident)
935 #fw('%s</PythonScript>\n' % ident)
938 fw('%s</Group>\n' % ident
)
940 ident
= writeTransform_end(ident
)
944 fw('%s</Collision>\n' % ident
)
946 def writeMaterial(ident
, material
, world
):
947 material_id
= quoteattr(unique_name(material
, MA_
+ material
.name
, uuid_cache_material
, clean_func
=clean_def
, sep
="_"))
949 # look up material name, use it if available
951 fw('%s<Material USE=%s />\n' % (ident
, material_id
))
956 ambient
= material
.ambient
/ 3.0
957 diffuseColor
= material
.diffuse_color
[:]
959 ambiColor
= ((material
.ambient
* 2.0) * world
.ambient_color
)[:]
961 ambiColor
= 0.0, 0.0, 0.0
963 emitColor
= tuple(((c
* emit
) + ambiColor
[i
]) / 2.0 for i
, c
in enumerate(diffuseColor
))
964 shininess
= material
.specular_hardness
/ 512.0
965 specColor
= tuple((c
+ 0.001) / (1.25 / (material
.specular_intensity
+ 0.001)) for c
in material
.specular_color
)
966 transp
= 1.0 - material
.alpha
968 if material
.use_shadeless
:
971 specColor
= emitColor
= diffuseColor
973 ident_step
= ident
+ (' ' * (-len(ident
) + \
974 fw('%s<Material ' % ident
)))
975 fw('DEF=%s\n' % material_id
)
976 fw(ident_step
+ 'diffuseColor="%.3f %.3f %.3f"\n' % clamp_color(diffuseColor
))
977 fw(ident_step
+ 'specularColor="%.3f %.3f %.3f"\n' % clamp_color(specColor
))
978 fw(ident_step
+ 'emissiveColor="%.3f %.3f %.3f"\n' % clamp_color(emitColor
))
979 fw(ident_step
+ 'ambientIntensity="%.3f"\n' % ambient
)
980 fw(ident_step
+ 'shininess="%.3f"\n' % shininess
)
981 fw(ident_step
+ 'transparency="%s"\n' % transp
)
982 fw(ident_step
+ '/>\n')
984 def writeMaterialH3D(ident
, material
, world
,
986 material_id
= quoteattr(unique_name(material
, 'MA_' + material
.name
, uuid_cache_material
, clean_func
=clean_def
, sep
="_"))
988 fw('%s<Material />\n' % ident
)
990 fw('%s<ComposedShader USE=%s />\n' % (ident
, material_id
))
994 # GPU_material_bind_uniforms
995 # GPU_begin_object_materials
1009 #~ GPU_DYNAMIC_LAMP_DYNCO 7
1010 #~ GPU_DYNAMIC_LAMP_DYNCOL 11
1011 #~ GPU_DYNAMIC_LAMP_DYNENERGY 10
1012 #~ GPU_DYNAMIC_LAMP_DYNIMAT 8
1013 #~ GPU_DYNAMIC_LAMP_DYNPERSMAT 9
1014 #~ GPU_DYNAMIC_LAMP_DYNVEC 6
1015 #~ GPU_DYNAMIC_OBJECT_COLOR 5
1016 #~ GPU_DYNAMIC_OBJECT_IMAT 4
1017 #~ GPU_DYNAMIC_OBJECT_MAT 2
1018 #~ GPU_DYNAMIC_OBJECT_VIEWIMAT 3
1019 #~ GPU_DYNAMIC_OBJECT_VIEWMAT 1
1020 #~ GPU_DYNAMIC_SAMPLER_2DBUFFER 12
1021 #~ GPU_DYNAMIC_SAMPLER_2DIMAGE 13
1022 #~ GPU_DYNAMIC_SAMPLER_2DSHADOW 14
1025 inline const char* typeToString( X3DType t ) {
1027 case SFFLOAT: return "SFFloat";
1028 case MFFLOAT: return "MFFloat";
1029 case SFDOUBLE: return "SFDouble";
1030 case MFDOUBLE: return "MFDouble";
1031 case SFTIME: return "SFTime";
1032 case MFTIME: return "MFTime";
1033 case SFINT32: return "SFInt32";
1034 case MFINT32: return "MFInt32";
1035 case SFVEC2F: return "SFVec2f";
1036 case MFVEC2F: return "MFVec2f";
1037 case SFVEC2D: return "SFVec2d";
1038 case MFVEC2D: return "MFVec2d";
1039 case SFVEC3F: return "SFVec3f";
1040 case MFVEC3F: return "MFVec3f";
1041 case SFVEC3D: return "SFVec3d";
1042 case MFVEC3D: return "MFVec3d";
1043 case SFVEC4F: return "SFVec4f";
1044 case MFVEC4F: return "MFVec4f";
1045 case SFVEC4D: return "SFVec4d";
1046 case MFVEC4D: return "MFVec4d";
1047 case SFBOOL: return "SFBool";
1048 case MFBOOL: return "MFBool";
1049 case SFSTRING: return "SFString";
1050 case MFSTRING: return "MFString";
1051 case SFNODE: return "SFNode";
1052 case MFNODE: return "MFNode";
1053 case SFCOLOR: return "SFColor";
1054 case MFCOLOR: return "MFColor";
1055 case SFCOLORRGBA: return "SFColorRGBA";
1056 case MFCOLORRGBA: return "MFColorRGBA";
1057 case SFROTATION: return "SFRotation";
1058 case MFROTATION: return "MFRotation";
1059 case SFQUATERNION: return "SFQuaternion";
1060 case MFQUATERNION: return "MFQuaternion";
1061 case SFMATRIX3F: return "SFMatrix3f";
1062 case MFMATRIX3F: return "MFMatrix3f";
1063 case SFMATRIX4F: return "SFMatrix4f";
1064 case MFMATRIX4F: return "MFMatrix4f";
1065 case SFMATRIX3D: return "SFMatrix3d";
1066 case MFMATRIX3D: return "MFMatrix3d";
1067 case SFMATRIX4D: return "SFMatrix4d";
1068 case MFMATRIX4D: return "MFMatrix4d";
1069 case UNKNOWN_X3D_TYPE:
1070 default:return "UNKNOWN_X3D_TYPE";
1074 fw('%s<ComposedShader DEF=%s language="GLSL" >\n' % (ident
, material_id
))
1077 shader_url_frag
= 'shaders/%s_%s.frag' % (filename_strip
, material_id
[1:-1])
1078 shader_url_vert
= 'shaders/%s_%s.vert' % (filename_strip
, material_id
[1:-1])
1081 shader_dir
= os
.path
.join(base_dst
, 'shaders')
1082 if not os
.path
.isdir(shader_dir
):
1083 os
.mkdir(shader_dir
)
1085 # ------------------------------------------------------
1087 field_descr
= " <!--- H3D View Matrix Patch -->"
1088 fw('%s<field name="%s" type="SFMatrix4f" accessType="inputOutput" />%s\n' % (ident
, H3D_VIEW_MATRIX
, field_descr
))
1089 frag_vars
= ["uniform mat4 %s;" % H3D_VIEW_MATRIX
]
1091 # annoying!, we need to track if some of the directional lamp
1092 # vars are children of the camera or not, since this adjusts how
1094 frag_uniform_var_map
= {}
1096 h3d_material_route
.append(
1097 '<ROUTE fromNode="%s" fromField="glModelViewMatrix" toNode=%s toField="%s" />%s' %
1098 (H3D_TOP_LEVEL
, material_id
, H3D_VIEW_MATRIX
, field_descr
))
1099 # ------------------------------------------------------
1101 for uniform
in gpu_shader
['uniforms']:
1102 if uniform
['type'] == gpu
.GPU_DYNAMIC_SAMPLER_2DIMAGE
:
1103 field_descr
= " <!--- Dynamic Sampler 2d Image -->"
1104 fw('%s<field name="%s" type="SFNode" accessType="inputOutput">%s\n' % (ident
, uniform
['varname'], field_descr
))
1105 writeImageTexture(ident
+ '\t', uniform
['image'])
1106 fw('%s</field>\n' % ident
)
1108 elif uniform
['type'] == gpu
.GPU_DYNAMIC_LAMP_DYNCO
:
1109 lamp_obj
= uniform
['lamp']
1110 frag_uniform_var_map
[uniform
['varname']] = lamp_obj
1112 if uniform
['datatype'] == gpu
.GPU_DATA_3F
: # should always be true!
1113 lamp_obj_id
= quoteattr(unique_name(lamp_obj
, LA_
+ lamp_obj
.name
, uuid_cache_lamp
, clean_func
=clean_def
, sep
="_"))
1114 lamp_obj_transform_id
= quoteattr(unique_name(lamp_obj
, lamp_obj
.name
, uuid_cache_object
, clean_func
=clean_def
, sep
="_"))
1116 value
= '%.6f %.6f %.6f' % (global_matrix
* lamp_obj
.matrix_world
).to_translation()[:]
1117 field_descr
= " <!--- Lamp DynCo '%s' -->" % lamp_obj
.name
1118 fw('%s<field name="%s" type="SFVec3f" accessType="inputOutput" value="%s" />%s\n' % (ident
, uniform
['varname'], value
, field_descr
))
1120 # ------------------------------------------------------
1122 field_descr
= " <!--- Lamp DynCo '%s' (shader patch) -->" % lamp_obj
.name
1123 fw('%s<field name="%s_transform" type="SFMatrix4f" accessType="inputOutput" />%s\n' % (ident
, uniform
['varname'], field_descr
))
1126 frag_vars
.append("uniform mat4 %s_transform;" % uniform
['varname'])
1127 h3d_material_route
.append(
1128 '<ROUTE fromNode=%s fromField="accumulatedForward" toNode=%s toField="%s_transform" />%s' %
1129 (suffix_quoted_str(lamp_obj_transform_id
, _TRANSFORM
), material_id
, uniform
['varname'], field_descr
))
1131 h3d_material_route
.append(
1132 '<ROUTE fromNode=%s fromField="location" toNode=%s toField="%s" /> %s' %
1133 (lamp_obj_id
, material_id
, uniform
['varname'], field_descr
))
1134 # ------------------------------------------------------
1139 elif uniform
['type'] == gpu
.GPU_DYNAMIC_LAMP_DYNCOL
:
1140 # odd we have both 3, 4 types.
1141 lamp_obj
= uniform
['lamp']
1142 frag_uniform_var_map
[uniform
['varname']] = lamp_obj
1144 lamp
= lamp_obj
.data
1145 value
= '%.6f %.6f %.6f' % (lamp
.color
* lamp
.energy
)[:]
1146 field_descr
= " <!--- Lamp DynColor '%s' -->" % lamp_obj
.name
1147 if uniform
['datatype'] == gpu
.GPU_DATA_3F
:
1148 fw('%s<field name="%s" type="SFVec3f" accessType="inputOutput" value="%s" />%s\n' % (ident
, uniform
['varname'], value
, field_descr
))
1149 elif uniform
['datatype'] == gpu
.GPU_DATA_4F
:
1150 fw('%s<field name="%s" type="SFVec4f" accessType="inputOutput" value="%s 1.0" />%s\n' % (ident
, uniform
['varname'], value
, field_descr
))
1154 elif uniform
['type'] == gpu
.GPU_DYNAMIC_LAMP_DYNENERGY
:
1158 elif uniform
['type'] == gpu
.GPU_DYNAMIC_LAMP_DYNVEC
:
1159 lamp_obj
= uniform
['lamp']
1160 frag_uniform_var_map
[uniform
['varname']] = lamp_obj
1162 if uniform
['datatype'] == gpu
.GPU_DATA_3F
:
1163 lamp_obj
= uniform
['lamp']
1164 value
= '%.6f %.6f %.6f' % ((global_matrix
* lamp_obj
.matrix_world
).to_quaternion() * mathutils
.Vector((0.0, 0.0, 1.0))).normalized()[:]
1165 field_descr
= " <!--- Lamp DynDirection '%s' -->" % lamp_obj
.name
1166 fw('%s<field name="%s" type="SFVec3f" accessType="inputOutput" value="%s" />%s\n' % (ident
, uniform
['varname'], value
, field_descr
))
1168 # route so we can have the lamp update the view
1169 if h3d_is_object_view(scene
, lamp_obj
):
1170 lamp_id
= quoteattr(unique_name(lamp_obj
, LA_
+ lamp_obj
.name
, uuid_cache_lamp
, clean_func
=clean_def
, sep
="_"))
1171 h3d_material_route
.append(
1172 '<ROUTE fromNode=%s fromField="direction" toNode=%s toField="%s" />%s' %
1173 (lamp_id
, material_id
, uniform
['varname'], field_descr
))
1178 elif uniform
['type'] == gpu
.GPU_DYNAMIC_OBJECT_VIEWIMAT
:
1179 frag_uniform_var_map
[uniform
['varname']] = None
1180 if uniform
['datatype'] == gpu
.GPU_DATA_16F
:
1181 field_descr
= " <!--- Object View Matrix Inverse '%s' -->" % obj
.name
1182 fw('%s<field name="%s" type="SFMatrix4f" accessType="inputOutput" />%s\n' % (ident
, uniform
['varname'], field_descr
))
1184 h3d_material_route
.append(
1185 '<ROUTE fromNode="%s" fromField="glModelViewMatrixInverse" toNode=%s toField="%s" />%s' %
1186 (H3D_TOP_LEVEL
, material_id
, uniform
['varname'], field_descr
))
1190 elif uniform
['type'] == gpu
.GPU_DYNAMIC_OBJECT_IMAT
:
1191 frag_uniform_var_map
[uniform
['varname']] = None
1192 if uniform
['datatype'] == gpu
.GPU_DATA_16F
:
1193 value
= ' '.join(['%.6f' % f
for v
in (global_matrix
* obj
.matrix_world
).inverted().transposed() for f
in v
])
1194 field_descr
= " <!--- Object Invertex Matrix '%s' -->" % obj
.name
1195 fw('%s<field name="%s" type="SFMatrix4f" accessType="inputOutput" value="%s" />%s\n' % (ident
, uniform
['varname'], value
, field_descr
))
1199 elif uniform
['type'] == gpu
.GPU_DYNAMIC_SAMPLER_2DSHADOW
:
1200 pass # XXX, shadow buffers not supported.
1202 elif uniform
['type'] == gpu
.GPU_DYNAMIC_SAMPLER_2DBUFFER
:
1203 frag_uniform_var_map
[uniform
['varname']] = None
1205 if uniform
['datatype'] == gpu
.GPU_DATA_1I
:
1207 tex
= uniform
['texpixels']
1209 for i
in range(0, len(tex
) - 1, 4):
1211 value
.append('0x%.2x%.2x%.2x%.2x' % (col
[0], col
[1], col
[2], col
[3]))
1213 field_descr
= " <!--- Material Buffer -->"
1214 fw('%s<field name="%s" type="SFNode" accessType="inputOutput">%s\n' % (ident
, uniform
['varname'], field_descr
))
1218 ident_step
= ident
+ (' ' * (-len(ident
) + \
1219 fw('%s<PixelTexture \n' % ident
)))
1220 fw(ident_step
+ 'repeatS="false"\n')
1221 fw(ident_step
+ 'repeatT="false"\n')
1223 fw(ident_step
+ 'image="%s 1 4 %s"\n' % (len(value
), " ".join(value
)))
1225 fw(ident_step
+ '/>\n')
1229 fw('%s</field>\n' % ident
)
1231 #for i in range(0, 10, 4)
1232 #value = ' '.join(['%d' % f for f in uniform['texpixels']])
1233 # value = ' '.join(['%.6f' % (f / 256) for f in uniform['texpixels']])
1235 #fw('%s<field name="%s" type="SFInt32" accessType="inputOutput" value="%s" />%s\n' % (ident, uniform['varname'], value, field_descr))
1236 #print('test', len(uniform['texpixels']))
1240 print("SKIPPING", uniform
['type'])
1242 file_frag
= open(os
.path
.join(base_dst
, shader_url_frag
), 'w', encoding
='utf-8')
1243 file_frag
.write(gpu_shader
['fragment'])
1246 h3d_shader_glsl_frag_patch(os
.path
.join(base_dst
, shader_url_frag
),
1249 frag_uniform_var_map
,
1252 file_vert
= open(os
.path
.join(base_dst
, shader_url_vert
), 'w', encoding
='utf-8')
1253 file_vert
.write(gpu_shader
['vertex'])
1256 fw('%s<ShaderPart type="FRAGMENT" url=%s />\n' % (ident
, quoteattr(shader_url_frag
)))
1257 fw('%s<ShaderPart type="VERTEX" url=%s />\n' % (ident
, quoteattr(shader_url_vert
)))
1260 fw('%s</ComposedShader>\n' % ident
)
1262 def writeImageTexture(ident
, image
):
1263 image_id
= quoteattr(unique_name(image
, IM_
+ image
.name
, uuid_cache_image
, clean_func
=clean_def
, sep
="_"))
1266 fw('%s<ImageTexture USE=%s />\n' % (ident
, image_id
))
1270 ident_step
= ident
+ (' ' * (-len(ident
) + \
1271 fw('%s<ImageTexture ' % ident
)))
1272 fw('DEF=%s\n' % image_id
)
1274 # collect image paths, can load multiple
1275 # [relative, name-only, absolute]
1276 filepath
= image
.filepath
1277 filepath_full
= bpy
.path
.abspath(filepath
, library
=image
.library
)
1278 filepath_ref
= bpy_extras
.io_utils
.path_reference(filepath_full
, base_src
, base_dst
, path_mode
, "textures", copy_set
, image
.library
)
1279 filepath_base
= os
.path
.basename(filepath_full
)
1285 if path_mode
!= 'RELATIVE':
1286 images
.append(filepath_full
)
1288 images
= [f
.replace('\\', '/') for f
in images
]
1289 images
= [f
for i
, f
in enumerate(images
) if f
not in images
[:i
]]
1291 fw(ident_step
+ "url='%s'\n" % ' '.join(['"%s"' % escape(f
) for f
in images
]))
1292 fw(ident_step
+ '/>\n')
1294 def writeBackground(ident
, world
):
1300 world_id
= quoteattr(unique_name(world
, WO_
+ world
.name
, uuid_cache_world
, clean_func
=clean_def
, sep
="_"))
1302 blending
= world
.use_sky_blend
, world
.use_sky_paper
, world
.use_sky_real
1304 grd_triple
= clamp_color(world
.horizon_color
)
1305 sky_triple
= clamp_color(world
.zenith_color
)
1306 mix_triple
= clamp_color((grd_triple
[i
] + sky_triple
[i
]) / 2.0 for i
in range(3))
1308 ident_step
= ident
+ (' ' * (-len(ident
) + \
1309 fw('%s<Background ' % ident
)))
1310 fw('DEF=%s\n' % world_id
)
1311 # No Skytype - just Hor color
1312 if blending
== (False, False, False):
1313 fw(ident_step
+ 'groundColor="%.3f %.3f %.3f"\n' % grd_triple
)
1314 fw(ident_step
+ 'skyColor="%.3f %.3f %.3f"\n' % grd_triple
)
1316 elif blending
== (True, False, False):
1317 fw(ident_step
+ 'groundColor="%.3f %.3f %.3f, %.3f %.3f %.3f"\n' % (grd_triple
+ mix_triple
))
1318 fw(ident_step
+ 'groundAngle="1.57"\n')
1319 fw(ident_step
+ 'skyColor="%.3f %.3f %.3f, %.3f %.3f %.3f"\n' % (sky_triple
+ mix_triple
))
1320 fw(ident_step
+ 'skyAngle="1.57"\n')
1321 # Blend+Real Gradient Inverse
1322 elif blending
== (True, False, True):
1323 fw(ident_step
+ 'groundColor="%.3f %.3f %.3f, %.3f %.3f %.3f"\n' % (sky_triple
+ grd_triple
))
1324 fw(ident_step
+ 'groundAngle="1.57"\n')
1325 fw(ident_step
+ 'skyColor="%.3f %.3f %.3f, %.3f %.3f %.3f, %.3f %.3f %.3f"\n' % (sky_triple
+ grd_triple
+ sky_triple
))
1326 fw(ident_step
+ 'skyAngle="1.57, 3.14159"\n')
1327 # Paper - just Zen Color
1328 elif blending
== (False, False, True):
1329 fw(ident_step
+ 'groundColor="%.3f %.3f %.3f"\n' % sky_triple
)
1330 fw(ident_step
+ 'skyColor="%.3f %.3f %.3f"\n' % sky_triple
)
1331 # Blend+Real+Paper - komplex gradient
1332 elif blending
== (True, True, True):
1333 fw(ident_step
+ 'groundColor="%.3f %.3f %.3f, %.3f %.3f %.3f"\n' % (sky_triple
+ grd_triple
))
1334 fw(ident_step
+ 'groundAngle="1.57"\n')
1335 fw(ident_step
+ 'skyColor="%.3f %.3f %.3f, %.3f %.3f %.3f"\n' % (sky_triple
+ grd_triple
))
1336 fw(ident_step
+ 'skyAngle="1.57"\n')
1337 # Any Other two colors
1339 fw(ident_step
+ 'groundColor="%.3f %.3f %.3f"\n' % grd_triple
)
1340 fw(ident_step
+ 'skyColor="%.3f %.3f %.3f"\n' % sky_triple
)
1342 for tex
in bpy
.data
.textures
:
1343 if tex
.type == 'IMAGE' and tex
.image
:
1346 basename
= quoteattr(bpy
.path
.basename(pic
.filepath
))
1348 if namemat
== 'back':
1349 fw(ident_step
+ 'backUrl=%s\n' % basename
)
1350 elif namemat
== 'bottom':
1351 fw(ident_step
+ 'bottomUrl=%s\n' % basename
)
1352 elif namemat
== 'front':
1353 fw(ident_step
+ 'frontUrl=%s\n' % basename
)
1354 elif namemat
== 'left':
1355 fw(ident_step
+ 'leftUrl=%s\n' % basename
)
1356 elif namemat
== 'right':
1357 fw(ident_step
+ 'rightUrl=%s\n' % basename
)
1358 elif namemat
== 'top':
1359 fw(ident_step
+ 'topUrl=%s\n' % basename
)
1361 fw(ident_step
+ '/>\n')
1363 # -------------------------------------------------------------------------
1364 # Export Object Hierarchy (recursively called)
1365 # -------------------------------------------------------------------------
1366 def export_object(ident
, obj_main_parent
, obj_main
, obj_children
):
1367 matrix_fallback
= mathutils
.Matrix()
1369 free
, derived
= create_derived_objects(scene
, obj_main
)
1372 obj_main_matrix_world
= obj_main
.matrix_world
1374 obj_main_matrix
= obj_main_parent
.matrix_world
.inverted(matrix_fallback
) * obj_main_matrix_world
1376 obj_main_matrix
= obj_main_matrix_world
1377 obj_main_matrix_world_invert
= obj_main_matrix_world
.inverted(matrix_fallback
)
1379 obj_main_id
= quoteattr(unique_name(obj_main
, obj_main
.name
, uuid_cache_object
, clean_func
=clean_def
, sep
="_"))
1381 ident
= writeTransform_begin(ident
, obj_main_matrix
if obj_main_parent
else global_matrix
* obj_main_matrix
, suffix_quoted_str(obj_main_id
, _TRANSFORM
))
1383 # Set here just incase we dont enter the loop below.
1386 for obj
, obj_matrix
in (() if derived
is None else derived
):
1390 # make transform node relative
1391 obj_matrix
= obj_main_matrix_world_invert
* obj_matrix
1393 obj_matrix
= global_matrix
* obj_matrix
1395 # H3D - use for writing a dummy transform parent
1398 if obj_type
== 'CAMERA':
1399 writeViewpoint(ident
, obj
, obj_matrix
, scene
)
1401 if use_h3d
and scene
.camera
== obj
:
1402 view_id
= uuid_cache_view
[obj
]
1403 fw('%s<Transform DEF="%s">\n' % (ident
, H3D_CAMERA_FOLLOW
))
1404 h3d_material_route
.extend([
1405 '<ROUTE fromNode="%s" fromField="totalPosition" toNode="%s" toField="translation" />' % (view_id
, H3D_CAMERA_FOLLOW
),
1406 '<ROUTE fromNode="%s" fromField="totalOrientation" toNode="%s" toField="rotation" />' % (view_id
, H3D_CAMERA_FOLLOW
),
1411 elif obj_type
in {'MESH', 'CURVE', 'SURFACE', 'FONT'}:
1412 if (obj_type
!= 'MESH') or (use_mesh_modifiers
and obj
.is_modified(scene
, 'PREVIEW')):
1414 me
= obj
.to_mesh(scene
, use_mesh_modifiers
, 'PREVIEW')
1423 # ensure unique name, we could also do this by
1424 # postponing mesh removal, but clearing data - TODO
1426 me
.name
= obj
.name
.rstrip("1234567890").rstrip(".")
1427 me_name_new
= me_name_org
= me
.name
1429 while me_name_new
in mesh_name_set
:
1430 me
.name
= "%.17s.%03d" % (me_name_org
, count
)
1431 me_name_new
= me
.name
1433 mesh_name_set
.add(me_name_new
)
1434 del me_name_new
, me_name_org
, count
1437 writeIndexedFaceSet(ident
, obj
, me
, obj_matrix
, world
)
1439 # free mesh created with create_mesh()
1441 bpy
.data
.meshes
.remove(me
)
1443 elif obj_type
== 'LAMP':
1445 datatype
= data
.type
1446 if datatype
== 'POINT':
1447 writePointLight(ident
, obj
, obj_matrix
, data
, world
)
1448 elif datatype
== 'SPOT':
1449 writeSpotLight(ident
, obj
, obj_matrix
, data
, world
)
1450 elif datatype
== 'SUN':
1451 writeDirectionalLight(ident
, obj
, obj_matrix
, data
, world
)
1453 writeDirectionalLight(ident
, obj
, obj_matrix
, data
, world
)
1455 #print "Info: Ignoring [%s], object type [%s] not handle yet" % (object.name,object.getType)
1459 free_derived_objects(obj_main
)
1461 # ---------------------------------------------------------------------
1462 # write out children recursively
1463 # ---------------------------------------------------------------------
1464 for obj_child
, obj_child_children
in obj_children
:
1465 export_object(ident
, obj_main
, obj_child
, obj_child_children
)
1469 fw('%s</Transform>\n' % ident
)
1473 ident
= writeTransform_end(ident
)
1475 # -------------------------------------------------------------------------
1476 # Main Export Function
1477 # -------------------------------------------------------------------------
1481 # tag un-exported IDs
1482 bpy
.data
.meshes
.tag(False)
1483 bpy
.data
.materials
.tag(False)
1484 bpy
.data
.images
.tag(False)
1487 objects
= [obj
for obj
in scene
.objects
if obj
.is_visible(scene
) and obj
.select
]
1489 objects
= [obj
for obj
in scene
.objects
if obj
.is_visible(scene
)]
1491 print('Info: starting X3D export to %r...' % file.name
)
1493 ident
= writeHeader(ident
)
1495 writeNavigationInfo(ident
, scene
, any(obj
.type == 'LAMP' for obj
in objects
))
1496 writeBackground(ident
, world
)
1497 writeFog(ident
, world
)
1502 objects_hierarchy
= build_hierarchy(objects
)
1504 objects_hierarchy
= ((obj
, []) for obj
in objects
)
1506 for obj_main
, obj_main_children
in objects_hierarchy
:
1507 export_object(ident
, None, obj_main
, obj_main_children
)
1509 ident
= writeFooter(ident
)
1513 # -------------------------------------------------------------------------
1515 # -------------------------------------------------------------------------
1519 bpy
.data
.materials
.remove(gpu_shader_dummy_mat
)
1521 # copy all collected files.
1523 bpy_extras
.io_utils
.path_reference_copy(copy_set
)
1525 print('Info: finished X3D export to %r' % file.name
)
1528 ##########################################################
1529 # Callbacks, needed before Main
1530 ##########################################################
1533 def gzip_open_utf8(filepath
, mode
):
1534 """Workaround for py3k only allowing binary gzip writing"""
1538 # need to investigate encoding
1539 file = gzip
.open(filepath
, mode
)
1540 write_real
= file.write
1542 def write_wrap(data
):
1543 return write_real(data
.encode("utf-8"))
1545 file.write
= write_wrap
1554 use_mesh_modifiers
=False,
1555 use_triangulate
=False,
1562 name_decorations
=True
1565 bpy
.path
.ensure_ext(filepath
, '.x3dz' if use_compress
else '.x3d')
1567 if bpy
.ops
.object.mode_set
.poll():
1568 bpy
.ops
.object.mode_set(mode
='OBJECT')
1571 file = gzip_open_utf8(filepath
, 'w')
1573 file = open(filepath
, 'w', encoding
='utf-8')
1575 if global_matrix
is None:
1576 global_matrix
= mathutils
.Matrix()
1581 use_mesh_modifiers
=use_mesh_modifiers
,
1582 use_selection
=use_selection
,
1583 use_triangulate
=use_triangulate
,
1584 use_normals
=use_normals
,
1585 use_hierarchy
=use_hierarchy
,
1587 path_mode
=path_mode
,
1588 name_decorations
=name_decorations
,