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 is_col
= (mesh
.tessface_vertex_colors
.active
and (material
is None or material
.use_vertex_color_paint
))
597 mesh_faces_col
= mesh
.tessface_vertex_colors
.active
.data
if is_col
else None
599 # Check if vertex colors can be exported in per-vertex mode.
600 # Do we have just one color per vertex in every face that uses the vertex?
602 def calc_vertex_color():
603 vert_color
= [None] * len(mesh
.vertices
)
605 for i
, face
in enumerate(mesh_faces
):
606 fcol
= mesh_faces_col
[i
]
607 face_colors
= (fcol
.color1
, fcol
.color2
, fcol
.color3
, fcol
.color4
)
608 for j
, vert_index
in enumerate(face
.vertices
):
609 if vert_color
[vert_index
] is None:
610 vert_color
[vert_index
] = face_colors
[j
][:]
611 elif vert_color
[vert_index
] != face_colors
[j
][:]:
614 return True, vert_color
616 is_col_per_vertex
, vert_color
= calc_vertex_color()
617 del calc_vertex_color
619 for (material_index
, image
), face_group
in face_groups_items
: # face_groups.items()
621 material
= mesh_materials
[material_index
]
623 fw('%s<Shape>\n' % ident
)
628 # kludge but as good as it gets!
630 if mesh_faces
[i
].use_smooth
:
634 # UV's and VCols split verts off which effects smoothing
635 # force writing normals in this case.
636 # Also, creaseAngle is not supported for IndexedTriangleSet,
637 # so write normals when is_smooth (otherwise
638 # IndexedTriangleSet can have only all smooth/all flat shading).
639 is_force_normals
= use_triangulate
and (is_smooth
or is_uv
or is_col
)
642 gpu_shader
= gpu_shader_cache
.get(material
) # material can be 'None', uses dummy cache
643 if gpu_shader
is None:
644 gpu_shader
= gpu_shader_cache
[material
] = gpu
.export_shader(scene
, material
)
647 gpu_shader_tmp
= gpu
.export_shader(scene
, material
)
649 print('\nWRITING MATERIAL:', material
.name
)
650 del gpu_shader_tmp
['fragment']
651 del gpu_shader_tmp
['vertex']
652 pprint
.pprint(gpu_shader_tmp
, width
=120)
653 #pprint.pprint(val['vertex'])
656 fw('%s<Appearance>\n' % ident
)
659 if image
and not use_h3d
:
660 writeImageTexture(ident
, image
)
662 if mesh_materials_use_face_texture
[material_index
]:
664 fw('%s<TextureTransform scale="%s %s" />\n' % (ident
, image
.tiles_x
, image
.tiles_y
))
667 loc
= mesh_material_mtex
[material_index
].offset
[:2]
669 # mtex_scale * tex_repeat
670 sca_x
, sca_y
= mesh_material_mtex
[material_index
].scale
[:2]
672 sca_x
*= mesh_material_tex
[material_index
].repeat_x
673 sca_y
*= mesh_material_tex
[material_index
].repeat_y
675 # flip x/y is a sampling feature, convert to transform
676 if mesh_material_tex
[material_index
].use_flip_axis
:
678 sca_x
, sca_y
= sca_y
, -sca_x
682 ident_step
= ident
+ (' ' * (-len(ident
) + \
683 fw('%s<TextureTransform ' % ident
)))
685 # fw('center="%.6f %.6f" ' % (0.0, 0.0))
686 fw(ident_step
+ 'translation="%.6f %.6f"\n' % loc
)
687 fw(ident_step
+ 'scale="%.6f %.6f"\n' % (sca_x
, sca_y
))
688 fw(ident_step
+ 'rotation="%.6f"\n' % rot
)
689 fw(ident_step
+ '/>\n')
692 mat_tmp
= material
if material
else gpu_shader_dummy_mat
693 writeMaterialH3D(ident
, mat_tmp
, world
,
698 writeMaterial(ident
, material
, world
)
701 fw('%s</Appearance>\n' % ident
)
703 mesh_faces_uv
= mesh
.tessface_uv_textures
.active
.data
if is_uv
else None
705 #-- IndexedFaceSet or IndexedLineSet
707 ident_step
= ident
+ (' ' * (-len(ident
) + \
708 fw('%s<IndexedTriangleSet ' % ident
)))
710 # --- Write IndexedTriangleSet Attributes (same as IndexedFaceSet)
711 fw('solid="%s"\n' % bool_as_str(material
and material
.game_settings
.use_backface_culling
))
713 if use_normals
or is_force_normals
:
714 fw(ident_step
+ 'normalPerVertex="true"\n')
716 # Tell X3D browser to generate flat (per-face) normals
717 fw(ident_step
+ 'normalPerVertex="false"\n')
726 def vertex_key(fidx
, f_cnr_idx
):
728 mesh_faces_uv
[fidx
].uv
[f_cnr_idx
][:],
729 getattr(mesh_faces_col
[fidx
], "color%d" % (f_cnr_idx
+ 1))[:],
734 def vertex_key(fidx
, f_cnr_idx
):
736 mesh_faces_uv
[fidx
].uv
[f_cnr_idx
][:],
741 def vertex_key(fidx
, f_cnr_idx
):
743 getattr(mesh_faces_col
[fidx
], "color%d" % (f_cnr_idx
+ 1))[:],
746 # ack, not especially efficient in this case
747 def vertex_key(fidx
, f_cnr_idx
):
750 # build a mesh mapping dict
751 vertex_hash
= [{} for i
in range(len(mesh
.vertices
))]
752 # worst case every face is a quad
753 face_tri_list
= [[None, None, None] for i
in range(len(mesh
.tessfaces
) * 2)]
757 temp_face
= [None] * 4
759 fv
= mesh_faces_vertices
[i
]
760 for j
, v_idx
in enumerate(fv
):
761 key
= vertex_key(i
, j
)
762 vh
= vertex_hash
[v_idx
]
765 x3d_v
= key
, v_idx
, totvert
767 # key / original_vertex / new_vertex
768 vert_tri_list
.append(x3d_v
)
773 f_iter
= ((0, 1, 2), (0, 2, 3))
775 f_iter
= ((0, 1, 2), )
778 # loop over a quad as 2 tris
779 f_tri
= face_tri_list
[totface
]
780 for ji
, j
in enumerate(f_it
):
781 f_tri
[ji
] = temp_face
[j
]
782 # quads run this twice
786 face_tri_list
[totface
:] = []
788 fw(ident_step
+ 'index="')
789 for x3d_f
in face_tri_list
:
790 fw('%i %i %i ' % (x3d_f
[0][2], x3d_f
[1][2], x3d_f
[2][2]))
793 # close IndexedTriangleSet
794 fw(ident_step
+ '>\n')
797 fw('%s<Coordinate ' % ident
)
799 for x3d_v
in vert_tri_list
:
800 fw('%.6f %.6f %.6f ' % mesh_vertices
[x3d_v
[1]].co
[:])
803 if use_normals
or is_force_normals
:
804 fw('%s<Normal ' % ident
)
806 for x3d_v
in vert_tri_list
:
807 fw('%.6f %.6f %.6f ' % mesh_vertices
[x3d_v
[1]].normal
[:])
811 fw('%s<TextureCoordinate point="' % ident
)
812 for x3d_v
in vert_tri_list
:
813 fw('%.4f %.4f ' % x3d_v
[0][slot_uv
])
817 fw('%s<Color color="' % ident
)
818 for x3d_v
in vert_tri_list
:
819 fw('%.3f %.3f %.3f ' % x3d_v
[0][slot_col
])
824 for gpu_attr
in gpu_shader
['attributes']:
827 if gpu_attr
['type'] == gpu
.CD_MTFACE
:
828 if gpu_attr
['datatype'] == gpu
.GPU_DATA_2F
:
829 fw('%s<FloatVertexAttribute ' % ident
)
830 fw('name="%s" ' % gpu_attr
['varname'])
831 fw('numComponents="2" ')
833 for x3d_v
in vert_tri_list
:
834 fw('%.4f %.4f ' % x3d_v
[0][slot_uv
])
839 elif gpu_attr
['type'] == gpu
.CD_MCOL
:
840 if gpu_attr
['datatype'] == gpu
.GPU_DATA_4UB
:
841 pass # XXX, H3D can't do
847 fw('%s</IndexedTriangleSet>\n' % ident
)
850 ident_step
= ident
+ (' ' * (-len(ident
) + \
851 fw('%s<IndexedFaceSet ' % ident
)))
853 # --- Write IndexedFaceSet Attributes (same as IndexedTriangleSet)
854 fw('solid="%s"\n' % bool_as_str(material
and material
.game_settings
.use_backface_culling
))
856 # use Auto-Smooth angle, if enabled. Otherwise make
857 # the mesh perfectly smooth by creaseAngle > pi.
858 fw(ident_step
+ 'creaseAngle="%.4f"\n' % (mesh
.auto_smooth_angle
if mesh
.use_auto_smooth
else 4.0))
861 # currently not optional, could be made so:
862 fw(ident_step
+ 'normalPerVertex="true"\n')
864 # IndexedTriangleSet assumes true
865 if is_col
and not is_col_per_vertex
:
866 fw(ident_step
+ 'colorPerVertex="false"\n')
868 # for IndexedTriangleSet we use a uv per vertex so this isnt needed.
870 fw(ident_step
+ 'texCoordIndex="')
874 if len(mesh_faces_vertices
[i
]) == 4:
875 fw('%d %d %d %d -1 ' % (j
, j
+ 1, j
+ 2, j
+ 3))
878 fw('%d %d %d -1 ' % (j
, j
+ 1, j
+ 2))
881 # --- end texCoordIndex
884 fw(ident_step
+ 'coordIndex="')
886 fv
= mesh_faces_vertices
[i
]
888 fw('%i %i %i -1 ' % fv
)
890 fw('%i %i %i %i -1 ' % fv
)
895 # close IndexedFaceSet
896 fw(ident_step
+ '>\n')
899 # --- Write IndexedFaceSet Elements
901 if is_coords_written
:
902 fw('%s<Coordinate USE=%s />\n' % (ident
, mesh_id_coords
))
904 fw('%s<Normal USE=%s />\n' % (ident
, mesh_id_normals
))
906 ident_step
= ident
+ (' ' * (-len(ident
) + \
907 fw('%s<Coordinate ' % ident
)))
908 fw('DEF=%s\n' % mesh_id_coords
)
909 fw(ident_step
+ 'point="')
910 for v
in mesh
.vertices
:
911 fw('%.6f %.6f %.6f ' % v
.co
[:])
913 fw(ident_step
+ '/>\n')
915 is_coords_written
= True
918 ident_step
= ident
+ (' ' * (-len(ident
) + \
919 fw('%s<Normal ' % ident
)))
920 fw('DEF=%s\n' % mesh_id_normals
)
921 fw(ident_step
+ 'vector="')
922 for v
in mesh
.vertices
:
923 fw('%.6f %.6f %.6f ' % v
.normal
[:])
925 fw(ident_step
+ '/>\n')
928 fw('%s<TextureCoordinate point="' % ident
)
930 for uv
in mesh_faces_uv
[i
].uv
:
931 fw('%.4f %.4f ' % uv
[:])
936 # Need better logic here, dynamic determination
937 # which of the X3D coloring models fits better this mesh - per face
938 # or per vertex. Probably with an explicit fallback mode parameter.
939 fw('%s<Color color="' % ident
)
940 if is_col_per_vertex
:
941 for i
in range(len(mesh
.vertices
)):
943 fw('%.3f %.3f %.3f ' % (vert_color
[i
] or (0.0, 0.0, 0.0)))
944 else: # Export as colors per face.
945 # TODO: average them rather than using the first one!
947 fw('%.3f %.3f %.3f ' % mesh_faces_col
[i
].color1
[:])
950 #--- output vertexColors
952 #--- output closing braces
955 fw('%s</IndexedFaceSet>\n' % ident
)
958 fw('%s</Shape>\n' % ident
)
962 #fw('%s<PythonScript DEF="PS" url="object.py" >\n' % ident)
963 #fw('%s <ShaderProgram USE="MA_Material.005" containerField="references"/>\n' % ident)
964 #fw('%s</PythonScript>\n' % ident)
967 fw('%s</Group>\n' % ident
)
969 ident
= writeTransform_end(ident
)
973 fw('%s</Collision>\n' % ident
)
975 def writeMaterial(ident
, material
, world
):
976 material_id
= quoteattr(unique_name(material
, MA_
+ material
.name
, uuid_cache_material
, clean_func
=clean_def
, sep
="_"))
978 # look up material name, use it if available
980 fw('%s<Material USE=%s />\n' % (ident
, material_id
))
985 ambient
= material
.ambient
/ 3.0
986 diffuseColor
= material
.diffuse_color
[:]
988 ambiColor
= ((material
.ambient
* 2.0) * world
.ambient_color
)[:]
990 ambiColor
= 0.0, 0.0, 0.0
992 emitColor
= tuple(((c
* emit
) + ambiColor
[i
]) / 2.0 for i
, c
in enumerate(diffuseColor
))
993 shininess
= material
.specular_hardness
/ 512.0
994 specColor
= tuple((c
+ 0.001) / (1.25 / (material
.specular_intensity
+ 0.001)) for c
in material
.specular_color
)
995 transp
= 1.0 - material
.alpha
997 if material
.use_shadeless
:
1000 specColor
= emitColor
= diffuseColor
1002 ident_step
= ident
+ (' ' * (-len(ident
) + \
1003 fw('%s<Material ' % ident
)))
1004 fw('DEF=%s\n' % material_id
)
1005 fw(ident_step
+ 'diffuseColor="%.3f %.3f %.3f"\n' % clamp_color(diffuseColor
))
1006 fw(ident_step
+ 'specularColor="%.3f %.3f %.3f"\n' % clamp_color(specColor
))
1007 fw(ident_step
+ 'emissiveColor="%.3f %.3f %.3f"\n' % clamp_color(emitColor
))
1008 fw(ident_step
+ 'ambientIntensity="%.3f"\n' % ambient
)
1009 fw(ident_step
+ 'shininess="%.3f"\n' % shininess
)
1010 fw(ident_step
+ 'transparency="%s"\n' % transp
)
1011 fw(ident_step
+ '/>\n')
1013 def writeMaterialH3D(ident
, material
, world
,
1015 material_id
= quoteattr(unique_name(material
, 'MA_' + material
.name
, uuid_cache_material
, clean_func
=clean_def
, sep
="_"))
1017 fw('%s<Material />\n' % ident
)
1019 fw('%s<ComposedShader USE=%s />\n' % (ident
, material_id
))
1023 # GPU_material_bind_uniforms
1024 # GPU_begin_object_materials
1038 #~ GPU_DYNAMIC_LAMP_DYNCO 7
1039 #~ GPU_DYNAMIC_LAMP_DYNCOL 11
1040 #~ GPU_DYNAMIC_LAMP_DYNENERGY 10
1041 #~ GPU_DYNAMIC_LAMP_DYNIMAT 8
1042 #~ GPU_DYNAMIC_LAMP_DYNPERSMAT 9
1043 #~ GPU_DYNAMIC_LAMP_DYNVEC 6
1044 #~ GPU_DYNAMIC_OBJECT_COLOR 5
1045 #~ GPU_DYNAMIC_OBJECT_IMAT 4
1046 #~ GPU_DYNAMIC_OBJECT_MAT 2
1047 #~ GPU_DYNAMIC_OBJECT_VIEWIMAT 3
1048 #~ GPU_DYNAMIC_OBJECT_VIEWMAT 1
1049 #~ GPU_DYNAMIC_SAMPLER_2DBUFFER 12
1050 #~ GPU_DYNAMIC_SAMPLER_2DIMAGE 13
1051 #~ GPU_DYNAMIC_SAMPLER_2DSHADOW 14
1054 inline const char* typeToString( X3DType t ) {
1056 case SFFLOAT: return "SFFloat";
1057 case MFFLOAT: return "MFFloat";
1058 case SFDOUBLE: return "SFDouble";
1059 case MFDOUBLE: return "MFDouble";
1060 case SFTIME: return "SFTime";
1061 case MFTIME: return "MFTime";
1062 case SFINT32: return "SFInt32";
1063 case MFINT32: return "MFInt32";
1064 case SFVEC2F: return "SFVec2f";
1065 case MFVEC2F: return "MFVec2f";
1066 case SFVEC2D: return "SFVec2d";
1067 case MFVEC2D: return "MFVec2d";
1068 case SFVEC3F: return "SFVec3f";
1069 case MFVEC3F: return "MFVec3f";
1070 case SFVEC3D: return "SFVec3d";
1071 case MFVEC3D: return "MFVec3d";
1072 case SFVEC4F: return "SFVec4f";
1073 case MFVEC4F: return "MFVec4f";
1074 case SFVEC4D: return "SFVec4d";
1075 case MFVEC4D: return "MFVec4d";
1076 case SFBOOL: return "SFBool";
1077 case MFBOOL: return "MFBool";
1078 case SFSTRING: return "SFString";
1079 case MFSTRING: return "MFString";
1080 case SFNODE: return "SFNode";
1081 case MFNODE: return "MFNode";
1082 case SFCOLOR: return "SFColor";
1083 case MFCOLOR: return "MFColor";
1084 case SFCOLORRGBA: return "SFColorRGBA";
1085 case MFCOLORRGBA: return "MFColorRGBA";
1086 case SFROTATION: return "SFRotation";
1087 case MFROTATION: return "MFRotation";
1088 case SFQUATERNION: return "SFQuaternion";
1089 case MFQUATERNION: return "MFQuaternion";
1090 case SFMATRIX3F: return "SFMatrix3f";
1091 case MFMATRIX3F: return "MFMatrix3f";
1092 case SFMATRIX4F: return "SFMatrix4f";
1093 case MFMATRIX4F: return "MFMatrix4f";
1094 case SFMATRIX3D: return "SFMatrix3d";
1095 case MFMATRIX3D: return "MFMatrix3d";
1096 case SFMATRIX4D: return "SFMatrix4d";
1097 case MFMATRIX4D: return "MFMatrix4d";
1098 case UNKNOWN_X3D_TYPE:
1099 default:return "UNKNOWN_X3D_TYPE";
1103 fw('%s<ComposedShader DEF=%s language="GLSL" >\n' % (ident
, material_id
))
1106 shader_url_frag
= 'shaders/%s_%s.frag' % (filename_strip
, material_id
[1:-1])
1107 shader_url_vert
= 'shaders/%s_%s.vert' % (filename_strip
, material_id
[1:-1])
1110 shader_dir
= os
.path
.join(base_dst
, 'shaders')
1111 if not os
.path
.isdir(shader_dir
):
1112 os
.mkdir(shader_dir
)
1114 # ------------------------------------------------------
1116 field_descr
= " <!--- H3D View Matrix Patch -->"
1117 fw('%s<field name="%s" type="SFMatrix4f" accessType="inputOutput" />%s\n' % (ident
, H3D_VIEW_MATRIX
, field_descr
))
1118 frag_vars
= ["uniform mat4 %s;" % H3D_VIEW_MATRIX
]
1120 # annoying!, we need to track if some of the directional lamp
1121 # vars are children of the camera or not, since this adjusts how
1123 frag_uniform_var_map
= {}
1125 h3d_material_route
.append(
1126 '<ROUTE fromNode="%s" fromField="glModelViewMatrix" toNode=%s toField="%s" />%s' %
1127 (H3D_TOP_LEVEL
, material_id
, H3D_VIEW_MATRIX
, field_descr
))
1128 # ------------------------------------------------------
1130 for uniform
in gpu_shader
['uniforms']:
1131 if uniform
['type'] == gpu
.GPU_DYNAMIC_SAMPLER_2DIMAGE
:
1132 field_descr
= " <!--- Dynamic Sampler 2d Image -->"
1133 fw('%s<field name="%s" type="SFNode" accessType="inputOutput">%s\n' % (ident
, uniform
['varname'], field_descr
))
1134 writeImageTexture(ident
+ '\t', uniform
['image'])
1135 fw('%s</field>\n' % ident
)
1137 elif uniform
['type'] == gpu
.GPU_DYNAMIC_LAMP_DYNCO
:
1138 lamp_obj
= uniform
['lamp']
1139 frag_uniform_var_map
[uniform
['varname']] = lamp_obj
1141 if uniform
['datatype'] == gpu
.GPU_DATA_3F
: # should always be true!
1142 lamp_obj_id
= quoteattr(unique_name(lamp_obj
, LA_
+ lamp_obj
.name
, uuid_cache_lamp
, clean_func
=clean_def
, sep
="_"))
1143 lamp_obj_transform_id
= quoteattr(unique_name(lamp_obj
, lamp_obj
.name
, uuid_cache_object
, clean_func
=clean_def
, sep
="_"))
1145 value
= '%.6f %.6f %.6f' % (global_matrix
* lamp_obj
.matrix_world
).to_translation()[:]
1146 field_descr
= " <!--- Lamp DynCo '%s' -->" % lamp_obj
.name
1147 fw('%s<field name="%s" type="SFVec3f" accessType="inputOutput" value="%s" />%s\n' % (ident
, uniform
['varname'], value
, field_descr
))
1149 # ------------------------------------------------------
1151 field_descr
= " <!--- Lamp DynCo '%s' (shader patch) -->" % lamp_obj
.name
1152 fw('%s<field name="%s_transform" type="SFMatrix4f" accessType="inputOutput" />%s\n' % (ident
, uniform
['varname'], field_descr
))
1155 frag_vars
.append("uniform mat4 %s_transform;" % uniform
['varname'])
1156 h3d_material_route
.append(
1157 '<ROUTE fromNode=%s fromField="accumulatedForward" toNode=%s toField="%s_transform" />%s' %
1158 (suffix_quoted_str(lamp_obj_transform_id
, _TRANSFORM
), material_id
, uniform
['varname'], field_descr
))
1160 h3d_material_route
.append(
1161 '<ROUTE fromNode=%s fromField="location" toNode=%s toField="%s" /> %s' %
1162 (lamp_obj_id
, material_id
, uniform
['varname'], field_descr
))
1163 # ------------------------------------------------------
1168 elif uniform
['type'] == gpu
.GPU_DYNAMIC_LAMP_DYNCOL
:
1169 # odd we have both 3, 4 types.
1170 lamp_obj
= uniform
['lamp']
1171 frag_uniform_var_map
[uniform
['varname']] = lamp_obj
1173 lamp
= lamp_obj
.data
1174 value
= '%.6f %.6f %.6f' % (lamp
.color
* lamp
.energy
)[:]
1175 field_descr
= " <!--- Lamp DynColor '%s' -->" % lamp_obj
.name
1176 if uniform
['datatype'] == gpu
.GPU_DATA_3F
:
1177 fw('%s<field name="%s" type="SFVec3f" accessType="inputOutput" value="%s" />%s\n' % (ident
, uniform
['varname'], value
, field_descr
))
1178 elif uniform
['datatype'] == gpu
.GPU_DATA_4F
:
1179 fw('%s<field name="%s" type="SFVec4f" accessType="inputOutput" value="%s 1.0" />%s\n' % (ident
, uniform
['varname'], value
, field_descr
))
1183 elif uniform
['type'] == gpu
.GPU_DYNAMIC_LAMP_DYNENERGY
:
1187 elif uniform
['type'] == gpu
.GPU_DYNAMIC_LAMP_DYNVEC
:
1188 lamp_obj
= uniform
['lamp']
1189 frag_uniform_var_map
[uniform
['varname']] = lamp_obj
1191 if uniform
['datatype'] == gpu
.GPU_DATA_3F
:
1192 lamp_obj
= uniform
['lamp']
1193 value
= '%.6f %.6f %.6f' % ((global_matrix
* lamp_obj
.matrix_world
).to_quaternion() * mathutils
.Vector((0.0, 0.0, 1.0))).normalized()[:]
1194 field_descr
= " <!--- Lamp DynDirection '%s' -->" % lamp_obj
.name
1195 fw('%s<field name="%s" type="SFVec3f" accessType="inputOutput" value="%s" />%s\n' % (ident
, uniform
['varname'], value
, field_descr
))
1197 # route so we can have the lamp update the view
1198 if h3d_is_object_view(scene
, lamp_obj
):
1199 lamp_id
= quoteattr(unique_name(lamp_obj
, LA_
+ lamp_obj
.name
, uuid_cache_lamp
, clean_func
=clean_def
, sep
="_"))
1200 h3d_material_route
.append(
1201 '<ROUTE fromNode=%s fromField="direction" toNode=%s toField="%s" />%s' %
1202 (lamp_id
, material_id
, uniform
['varname'], field_descr
))
1207 elif uniform
['type'] == gpu
.GPU_DYNAMIC_OBJECT_VIEWIMAT
:
1208 frag_uniform_var_map
[uniform
['varname']] = None
1209 if uniform
['datatype'] == gpu
.GPU_DATA_16F
:
1210 field_descr
= " <!--- Object View Matrix Inverse '%s' -->" % obj
.name
1211 fw('%s<field name="%s" type="SFMatrix4f" accessType="inputOutput" />%s\n' % (ident
, uniform
['varname'], field_descr
))
1213 h3d_material_route
.append(
1214 '<ROUTE fromNode="%s" fromField="glModelViewMatrixInverse" toNode=%s toField="%s" />%s' %
1215 (H3D_TOP_LEVEL
, material_id
, uniform
['varname'], field_descr
))
1219 elif uniform
['type'] == gpu
.GPU_DYNAMIC_OBJECT_IMAT
:
1220 frag_uniform_var_map
[uniform
['varname']] = None
1221 if uniform
['datatype'] == gpu
.GPU_DATA_16F
:
1222 value
= ' '.join(['%.6f' % f
for v
in (global_matrix
* obj
.matrix_world
).inverted().transposed() for f
in v
])
1223 field_descr
= " <!--- Object Invertex Matrix '%s' -->" % obj
.name
1224 fw('%s<field name="%s" type="SFMatrix4f" accessType="inputOutput" value="%s" />%s\n' % (ident
, uniform
['varname'], value
, field_descr
))
1228 elif uniform
['type'] == gpu
.GPU_DYNAMIC_SAMPLER_2DSHADOW
:
1229 pass # XXX, shadow buffers not supported.
1231 elif uniform
['type'] == gpu
.GPU_DYNAMIC_SAMPLER_2DBUFFER
:
1232 frag_uniform_var_map
[uniform
['varname']] = None
1234 if uniform
['datatype'] == gpu
.GPU_DATA_1I
:
1236 tex
= uniform
['texpixels']
1238 for i
in range(0, len(tex
) - 1, 4):
1240 value
.append('0x%.2x%.2x%.2x%.2x' % (col
[0], col
[1], col
[2], col
[3]))
1242 field_descr
= " <!--- Material Buffer -->"
1243 fw('%s<field name="%s" type="SFNode" accessType="inputOutput">%s\n' % (ident
, uniform
['varname'], field_descr
))
1247 ident_step
= ident
+ (' ' * (-len(ident
) + \
1248 fw('%s<PixelTexture \n' % ident
)))
1249 fw(ident_step
+ 'repeatS="false"\n')
1250 fw(ident_step
+ 'repeatT="false"\n')
1252 fw(ident_step
+ 'image="%s 1 4 %s"\n' % (len(value
), " ".join(value
)))
1254 fw(ident_step
+ '/>\n')
1258 fw('%s</field>\n' % ident
)
1260 #for i in range(0, 10, 4)
1261 #value = ' '.join(['%d' % f for f in uniform['texpixels']])
1262 # value = ' '.join(['%.6f' % (f / 256) for f in uniform['texpixels']])
1264 #fw('%s<field name="%s" type="SFInt32" accessType="inputOutput" value="%s" />%s\n' % (ident, uniform['varname'], value, field_descr))
1265 #print('test', len(uniform['texpixels']))
1269 print("SKIPPING", uniform
['type'])
1271 file_frag
= open(os
.path
.join(base_dst
, shader_url_frag
), 'w', encoding
='utf-8')
1272 file_frag
.write(gpu_shader
['fragment'])
1275 h3d_shader_glsl_frag_patch(os
.path
.join(base_dst
, shader_url_frag
),
1278 frag_uniform_var_map
,
1281 file_vert
= open(os
.path
.join(base_dst
, shader_url_vert
), 'w', encoding
='utf-8')
1282 file_vert
.write(gpu_shader
['vertex'])
1285 fw('%s<ShaderPart type="FRAGMENT" url=%s />\n' % (ident
, quoteattr(shader_url_frag
)))
1286 fw('%s<ShaderPart type="VERTEX" url=%s />\n' % (ident
, quoteattr(shader_url_vert
)))
1289 fw('%s</ComposedShader>\n' % ident
)
1291 def writeImageTexture(ident
, image
):
1292 image_id
= quoteattr(unique_name(image
, IM_
+ image
.name
, uuid_cache_image
, clean_func
=clean_def
, sep
="_"))
1295 fw('%s<ImageTexture USE=%s />\n' % (ident
, image_id
))
1299 ident_step
= ident
+ (' ' * (-len(ident
) + \
1300 fw('%s<ImageTexture ' % ident
)))
1301 fw('DEF=%s\n' % image_id
)
1303 # collect image paths, can load multiple
1304 # [relative, name-only, absolute]
1305 filepath
= image
.filepath
1306 filepath_full
= bpy
.path
.abspath(filepath
, library
=image
.library
)
1307 filepath_ref
= bpy_extras
.io_utils
.path_reference(filepath_full
, base_src
, base_dst
, path_mode
, "textures", copy_set
, image
.library
)
1308 filepath_base
= os
.path
.basename(filepath_full
)
1314 if path_mode
!= 'RELATIVE':
1315 images
.append(filepath_full
)
1317 images
= [f
.replace('\\', '/') for f
in images
]
1318 images
= [f
for i
, f
in enumerate(images
) if f
not in images
[:i
]]
1320 fw(ident_step
+ "url='%s'\n" % ' '.join(['"%s"' % escape(f
) for f
in images
]))
1321 fw(ident_step
+ '/>\n')
1323 def writeBackground(ident
, world
):
1329 world_id
= quoteattr(unique_name(world
, WO_
+ world
.name
, uuid_cache_world
, clean_func
=clean_def
, sep
="_"))
1331 blending
= world
.use_sky_blend
, world
.use_sky_paper
, world
.use_sky_real
1333 grd_triple
= clamp_color(world
.horizon_color
)
1334 sky_triple
= clamp_color(world
.zenith_color
)
1335 mix_triple
= clamp_color((grd_triple
[i
] + sky_triple
[i
]) / 2.0 for i
in range(3))
1337 ident_step
= ident
+ (' ' * (-len(ident
) + \
1338 fw('%s<Background ' % ident
)))
1339 fw('DEF=%s\n' % world_id
)
1340 # No Skytype - just Hor color
1341 if blending
== (False, False, False):
1342 fw(ident_step
+ 'groundColor="%.3f %.3f %.3f"\n' % grd_triple
)
1343 fw(ident_step
+ 'skyColor="%.3f %.3f %.3f"\n' % grd_triple
)
1345 elif blending
== (True, False, False):
1346 fw(ident_step
+ 'groundColor="%.3f %.3f %.3f, %.3f %.3f %.3f"\n' % (grd_triple
+ mix_triple
))
1347 fw(ident_step
+ 'groundAngle="1.57"\n')
1348 fw(ident_step
+ 'skyColor="%.3f %.3f %.3f, %.3f %.3f %.3f"\n' % (sky_triple
+ mix_triple
))
1349 fw(ident_step
+ 'skyAngle="1.57"\n')
1350 # Blend+Real Gradient Inverse
1351 elif blending
== (True, False, True):
1352 fw(ident_step
+ 'groundColor="%.3f %.3f %.3f, %.3f %.3f %.3f"\n' % (sky_triple
+ grd_triple
))
1353 fw(ident_step
+ 'groundAngle="1.57"\n')
1354 fw(ident_step
+ 'skyColor="%.3f %.3f %.3f, %.3f %.3f %.3f, %.3f %.3f %.3f"\n' % (sky_triple
+ grd_triple
+ sky_triple
))
1355 fw(ident_step
+ 'skyAngle="1.57, 3.14159"\n')
1356 # Paper - just Zen Color
1357 elif blending
== (False, False, True):
1358 fw(ident_step
+ 'groundColor="%.3f %.3f %.3f"\n' % sky_triple
)
1359 fw(ident_step
+ 'skyColor="%.3f %.3f %.3f"\n' % sky_triple
)
1360 # Blend+Real+Paper - komplex gradient
1361 elif blending
== (True, True, True):
1362 fw(ident_step
+ 'groundColor="%.3f %.3f %.3f, %.3f %.3f %.3f"\n' % (sky_triple
+ grd_triple
))
1363 fw(ident_step
+ 'groundAngle="1.57"\n')
1364 fw(ident_step
+ 'skyColor="%.3f %.3f %.3f, %.3f %.3f %.3f"\n' % (sky_triple
+ grd_triple
))
1365 fw(ident_step
+ 'skyAngle="1.57"\n')
1366 # Any Other two colors
1368 fw(ident_step
+ 'groundColor="%.3f %.3f %.3f"\n' % grd_triple
)
1369 fw(ident_step
+ 'skyColor="%.3f %.3f %.3f"\n' % sky_triple
)
1371 for tex
in bpy
.data
.textures
:
1372 if tex
.type == 'IMAGE' and tex
.image
:
1375 basename
= quoteattr(bpy
.path
.basename(pic
.filepath
))
1377 if namemat
== 'back':
1378 fw(ident_step
+ 'backUrl=%s\n' % basename
)
1379 elif namemat
== 'bottom':
1380 fw(ident_step
+ 'bottomUrl=%s\n' % basename
)
1381 elif namemat
== 'front':
1382 fw(ident_step
+ 'frontUrl=%s\n' % basename
)
1383 elif namemat
== 'left':
1384 fw(ident_step
+ 'leftUrl=%s\n' % basename
)
1385 elif namemat
== 'right':
1386 fw(ident_step
+ 'rightUrl=%s\n' % basename
)
1387 elif namemat
== 'top':
1388 fw(ident_step
+ 'topUrl=%s\n' % basename
)
1390 fw(ident_step
+ '/>\n')
1392 # -------------------------------------------------------------------------
1393 # Export Object Hierarchy (recursively called)
1394 # -------------------------------------------------------------------------
1395 def export_object(ident
, obj_main_parent
, obj_main
, obj_children
):
1396 matrix_fallback
= mathutils
.Matrix()
1398 free
, derived
= create_derived_objects(scene
, obj_main
)
1401 obj_main_matrix_world
= obj_main
.matrix_world
1403 obj_main_matrix
= obj_main_parent
.matrix_world
.inverted(matrix_fallback
) * obj_main_matrix_world
1405 obj_main_matrix
= obj_main_matrix_world
1406 obj_main_matrix_world_invert
= obj_main_matrix_world
.inverted(matrix_fallback
)
1408 obj_main_id
= quoteattr(unique_name(obj_main
, obj_main
.name
, uuid_cache_object
, clean_func
=clean_def
, sep
="_"))
1410 ident
= writeTransform_begin(ident
, obj_main_matrix
if obj_main_parent
else global_matrix
* obj_main_matrix
, suffix_quoted_str(obj_main_id
, _TRANSFORM
))
1412 # Set here just incase we dont enter the loop below.
1415 for obj
, obj_matrix
in (() if derived
is None else derived
):
1419 # make transform node relative
1420 obj_matrix
= obj_main_matrix_world_invert
* obj_matrix
1422 obj_matrix
= global_matrix
* obj_matrix
1424 # H3D - use for writing a dummy transform parent
1427 if obj_type
== 'CAMERA':
1428 writeViewpoint(ident
, obj
, obj_matrix
, scene
)
1430 if use_h3d
and scene
.camera
== obj
:
1431 view_id
= uuid_cache_view
[obj
]
1432 fw('%s<Transform DEF="%s">\n' % (ident
, H3D_CAMERA_FOLLOW
))
1433 h3d_material_route
.extend([
1434 '<ROUTE fromNode="%s" fromField="totalPosition" toNode="%s" toField="translation" />' % (view_id
, H3D_CAMERA_FOLLOW
),
1435 '<ROUTE fromNode="%s" fromField="totalOrientation" toNode="%s" toField="rotation" />' % (view_id
, H3D_CAMERA_FOLLOW
),
1440 elif obj_type
in {'MESH', 'CURVE', 'SURFACE', 'FONT'}:
1441 if (obj_type
!= 'MESH') or (use_mesh_modifiers
and obj
.is_modified(scene
, 'PREVIEW')):
1443 me
= obj
.to_mesh(scene
, use_mesh_modifiers
, 'PREVIEW')
1452 # ensure unique name, we could also do this by
1453 # postponing mesh removal, but clearing data - TODO
1455 me
.name
= obj
.name
.rstrip("1234567890").rstrip(".")
1456 me_name_new
= me_name_org
= me
.name
1458 while me_name_new
in mesh_name_set
:
1459 me
.name
= "%.17s.%03d" % (me_name_org
, count
)
1460 me_name_new
= me
.name
1462 mesh_name_set
.add(me_name_new
)
1463 del me_name_new
, me_name_org
, count
1466 writeIndexedFaceSet(ident
, obj
, me
, obj_matrix
, world
)
1468 # free mesh created with create_mesh()
1470 bpy
.data
.meshes
.remove(me
)
1472 elif obj_type
== 'LAMP':
1474 datatype
= data
.type
1475 if datatype
== 'POINT':
1476 writePointLight(ident
, obj
, obj_matrix
, data
, world
)
1477 elif datatype
== 'SPOT':
1478 writeSpotLight(ident
, obj
, obj_matrix
, data
, world
)
1479 elif datatype
== 'SUN':
1480 writeDirectionalLight(ident
, obj
, obj_matrix
, data
, world
)
1482 writeDirectionalLight(ident
, obj
, obj_matrix
, data
, world
)
1484 #print "Info: Ignoring [%s], object type [%s] not handle yet" % (object.name,object.getType)
1488 free_derived_objects(obj_main
)
1490 # ---------------------------------------------------------------------
1491 # write out children recursively
1492 # ---------------------------------------------------------------------
1493 for obj_child
, obj_child_children
in obj_children
:
1494 export_object(ident
, obj_main
, obj_child
, obj_child_children
)
1498 fw('%s</Transform>\n' % ident
)
1502 ident
= writeTransform_end(ident
)
1504 # -------------------------------------------------------------------------
1505 # Main Export Function
1506 # -------------------------------------------------------------------------
1510 # tag un-exported IDs
1511 bpy
.data
.meshes
.tag(False)
1512 bpy
.data
.materials
.tag(False)
1513 bpy
.data
.images
.tag(False)
1516 objects
= [obj
for obj
in scene
.objects
if obj
.is_visible(scene
) and obj
.select
]
1518 objects
= [obj
for obj
in scene
.objects
if obj
.is_visible(scene
)]
1520 print('Info: starting X3D export to %r...' % file.name
)
1522 ident
= writeHeader(ident
)
1524 writeNavigationInfo(ident
, scene
, any(obj
.type == 'LAMP' for obj
in objects
))
1525 writeBackground(ident
, world
)
1526 writeFog(ident
, world
)
1531 objects_hierarchy
= build_hierarchy(objects
)
1533 objects_hierarchy
= ((obj
, []) for obj
in objects
)
1535 for obj_main
, obj_main_children
in objects_hierarchy
:
1536 export_object(ident
, None, obj_main
, obj_main_children
)
1538 ident
= writeFooter(ident
)
1542 # -------------------------------------------------------------------------
1544 # -------------------------------------------------------------------------
1548 bpy
.data
.materials
.remove(gpu_shader_dummy_mat
)
1550 # copy all collected files.
1552 bpy_extras
.io_utils
.path_reference_copy(copy_set
)
1554 print('Info: finished X3D export to %r' % file.name
)
1557 ##########################################################
1558 # Callbacks, needed before Main
1559 ##########################################################
1562 def gzip_open_utf8(filepath
, mode
):
1563 """Workaround for py3k only allowing binary gzip writing"""
1567 # need to investigate encoding
1568 file = gzip
.open(filepath
, mode
)
1569 write_real
= file.write
1571 def write_wrap(data
):
1572 return write_real(data
.encode("utf-8"))
1574 file.write
= write_wrap
1583 use_mesh_modifiers
=False,
1584 use_triangulate
=False,
1591 name_decorations
=True
1594 bpy
.path
.ensure_ext(filepath
, '.x3dz' if use_compress
else '.x3d')
1596 if bpy
.ops
.object.mode_set
.poll():
1597 bpy
.ops
.object.mode_set(mode
='OBJECT')
1600 file = gzip_open_utf8(filepath
, 'w')
1602 file = open(filepath
, 'w', encoding
='utf-8')
1604 if global_matrix
is None:
1605 global_matrix
= mathutils
.Matrix()
1610 use_mesh_modifiers
=use_mesh_modifiers
,
1611 use_selection
=use_selection
,
1612 use_triangulate
=use_triangulate
,
1613 use_normals
=use_normals
,
1614 use_hierarchy
=use_hierarchy
,
1616 path_mode
=path_mode
,
1617 name_decorations
=name_decorations
,