1 # SPDX-License-Identifier: GPL-2.0-or-later
3 # Contributors: bart:neeneenee*de, http://www.neeneenee.de/vrml, Campbell Barton
6 This script exports to X3D format.
9 Run this script from "File->Export" menu. A pop-up will ask whether you
10 want to export only selected or all relevant objects.
13 Doesn't handle multiple materials (don't use material indices);<br>
14 Doesn't handle multiple UV textures on a single mesh (create a mesh for each texture);<br>
15 Can't get the texture array associated with material * not the UV ones;
24 from bpy_extras
.io_utils
import create_derived_objects
28 H3D_TOP_LEVEL
= 'TOP_LEVEL_TI'
29 H3D_CAMERA_FOLLOW
= 'CAMERA_FOLLOW_TRANSFORM'
30 H3D_VIEW_MATRIX
= 'view_matrix'
34 return tuple([max(min(c
, 1.0), 0.0) for c
in col
])
37 def matrix_direction_neg_z(matrix
):
38 return (matrix
.to_3x3() @ mathutils
.Vector((0.0, 0.0, -1.0))).normalized()[:]
41 def prefix_quoted_str(value
, prefix
):
42 return value
[0] + prefix
+ value
[1:]
45 def suffix_quoted_str(value
, suffix
):
46 return value
[:-1] + suffix
+ value
[-1:]
49 def bool_as_str(value
):
50 return ('false', 'true')[bool(value
)]
58 if txt
[0] in "1234567890+-":
60 return txt
.translate({
61 # control characters 0x0-0x1f
111 def build_hierarchy(objects
):
112 """ returns parent child relationships, skipping
114 objects_set
= set(objects
)
117 def test_parent(parent
):
118 while (parent
is not None) and (parent
not in objects_set
):
119 parent
= parent
.parent
123 par_lookup
.setdefault(test_parent(obj
.parent
), []).append((obj
, []))
125 for parent
, children
in par_lookup
.items():
126 for obj
, subchildren
in children
:
127 subchildren
[:] = par_lookup
.get(obj
, [])
129 return par_lookup
.get(None, [])
132 # -----------------------------------------------------------------------------
134 # -----------------------------------------------------------------------------
135 def h3d_shader_glsl_frag_patch(filepath
, scene
, global_vars
, frag_uniform_var_map
):
136 h3d_file
= open(filepath
, 'r', encoding
='utf-8')
139 last_transform
= None
142 if l
.startswith("void main(void)"):
144 lines
.append("// h3d custom vars begin\n")
145 for v
in global_vars
:
146 lines
.append("%s\n" % v
)
147 lines
.append("// h3d custom vars end\n")
149 elif l
.lstrip().startswith("light_visibility_other("):
151 last_transform
= w
[1] + "_transform" # XXX - HACK!!!
152 w
[1] = '(view_matrix * %s_transform * vec4(%s.x, %s.y, %s.z, 1.0)).xyz' % (w
[1], w
[1], w
[1], w
[1])
154 elif l
.lstrip().startswith("light_visibility_sun_hemi("):
156 w
[0] = w
[0][len("light_visibility_sun_hemi(") + 1:]
158 if not h3d_is_object_view(scene
, frag_uniform_var_map
[w
[0]]):
159 w
[0] = '(mat3(normalize(view_matrix[0].xyz), normalize(view_matrix[1].xyz), normalize(view_matrix[2].xyz)) * -%s)' % w
[0]
161 w
[0] = ('(mat3(normalize((view_matrix*%s)[0].xyz), normalize((view_matrix*%s)[1].xyz), normalize((view_matrix*%s)[2].xyz)) * -%s)' %
162 (last_transform
, last_transform
, last_transform
, w
[0]))
164 l
= "\tlight_visibility_sun_hemi(" + ", ".join(w
)
165 elif l
.lstrip().startswith("light_visibility_spot_circle("):
167 w
[0] = w
[0][len("light_visibility_spot_circle(") + 1:]
169 if not h3d_is_object_view(scene
, frag_uniform_var_map
[w
[0]]):
170 w
[0] = '(mat3(normalize(view_matrix[0].xyz), normalize(view_matrix[1].xyz), normalize(view_matrix[2].xyz)) * -%s)' % w
[0]
172 w
[0] = ('(mat3(normalize((view_matrix*%s)[0].xyz), normalize((view_matrix*%s)[1].xyz), normalize((view_matrix*%s)[2].xyz)) * %s)' %
173 (last_transform
, last_transform
, last_transform
, w
[0]))
175 l
= "\tlight_visibility_spot_circle(" + ", ".join(w
)
181 h3d_file
= open(filepath
, 'w', encoding
='utf-8')
182 h3d_file
.writelines(lines
)
186 def h3d_is_object_view(scene
, obj
):
187 camera
= scene
.camera
192 parent
= parent
.parent
196 # -----------------------------------------------------------------------------
197 # Functions for writing output file
198 # -----------------------------------------------------------------------------
205 use_mesh_modifiers
=False,
207 use_triangulate
=False,
212 name_decorations
=True,
215 # -------------------------------------------------------------------------
217 # -------------------------------------------------------------------------
219 from bpy_extras
.io_utils
import unique_name
220 from xml
.sax
.saxutils
import quoteattr
, escape
223 # If names are decorated, the uuid map can be split up
224 # by type for efficiency of collision testing
225 # since objects of different types will always have
226 # different decorated names.
227 uuid_cache_object
= {} # object
228 uuid_cache_light
= {} # 'LA_' + object.name
229 uuid_cache_view
= {} # object, different namespace
230 uuid_cache_mesh
= {} # mesh
231 uuid_cache_material
= {} # material
232 uuid_cache_image
= {} # image
233 uuid_cache_world
= {} # world
243 # If names are not decorated, it may be possible for two objects to
244 # have the same name, so there has to be a unified dictionary to
245 # prevent uuid collisions.
247 uuid_cache_object
= uuid_cache
# object
248 uuid_cache_light
= uuid_cache
# 'LA_' + object.name
249 uuid_cache_view
= uuid_cache
# object, different namespace
250 uuid_cache_mesh
= uuid_cache
# mesh
251 uuid_cache_material
= uuid_cache
# material
252 uuid_cache_image
= uuid_cache
# image
253 uuid_cache_world
= uuid_cache
# world
264 _TRANSFORM
= '_TRANSFORM'
266 # store files to copy
269 # store names of newly created meshes, so we dont overlap
270 mesh_name_set
= set()
273 base_src
= os
.path
.dirname(bpy
.data
.filepath
)
274 base_dst
= os
.path
.dirname(file.name
)
275 filename_strip
= os
.path
.splitext(os
.path
.basename(file.name
))[0]
276 gpu_shader_cache
= {}
280 gpu_shader_dummy_mat
= bpy
.data
.materials
.new('X3D_DYMMY_MAT')
281 gpu_shader_cache
[None] = gpu
.export_shader(scene
, gpu_shader_dummy_mat
)
282 h3d_material_route
= []
284 # -------------------------------------------------------------------------
285 # File Writing Functions
286 # -------------------------------------------------------------------------
288 def writeHeader(ident
):
289 filepath_quoted
= quoteattr(os
.path
.basename(file.name
))
290 blender_ver_quoted
= quoteattr('Blender %s' % bpy
.app
.version_string
)
292 fw('%s<?xml version="1.0" encoding="UTF-8"?>\n' % ident
)
294 fw('%s<X3D profile="H3DAPI" version="1.4">\n' % ident
)
296 fw('%s<!DOCTYPE X3D PUBLIC "ISO//Web3D//DTD X3D 3.0//EN" "http://www.web3d.org/specifications/x3d-3.0.dtd">\n' % ident
)
297 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
)
300 fw('%s<head>\n' % ident
)
302 fw('%s<meta name="filename" content=%s />\n' % (ident
, filepath_quoted
))
303 fw('%s<meta name="generator" content=%s />\n' % (ident
, blender_ver_quoted
))
304 # this info was never updated, so blender version should be enough
305 # fw('%s<meta name="translator" content="X3D exporter v1.55 (2006/01/17)" />\n' % ident)
307 fw('%s</head>\n' % ident
)
308 fw('%s<Scene>\n' % ident
)
312 # outputs the view matrix in glModelViewMatrix field
313 fw('%s<TransformInfo DEF="%s" outputGLMatrices="true" />\n' % (ident
, H3D_TOP_LEVEL
))
317 def writeFooter(ident
):
321 for route
in h3d_material_route
:
322 fw('%s%s\n' % (ident
, route
))
325 fw('%s</Scene>\n' % ident
)
327 fw('%s</X3D>' % ident
)
330 def writeViewpoint(ident
, obj
, matrix
, scene
):
331 view_id
= quoteattr(unique_name(obj
, CA_
+ obj
.name
, uuid_cache_view
, clean_func
=clean_def
, sep
="_"))
333 loc
, rot
, scale
= matrix
.decompose()
334 rot
= rot
.to_axis_angle()
335 rot
= (*rot
[0].normalized(), rot
[1])
337 ident_step
= ident
+ (' ' * (-len(ident
) + \
338 fw('%s<Viewpoint ' % ident
)))
339 fw('DEF=%s\n' % view_id
)
340 fw(ident_step
+ 'centerOfRotation="0 0 0"\n')
341 fw(ident_step
+ 'position="%3.2f %3.2f %3.2f"\n' % loc
[:])
342 fw(ident_step
+ 'orientation="%3.2f %3.2f %3.2f %3.2f"\n' % rot
)
343 fw(ident_step
+ 'fieldOfView="%.3f"\n' % obj
.data
.angle
)
344 fw(ident_step
+ '/>\n')
346 def writeFog(ident
, world
):
348 mtype
= world
.mist_settings
.falloff
349 mparam
= world
.mist_settings
354 ident_step
= ident
+ (' ' * (-len(ident
) + \
355 fw('%s<Fog ' % ident
)))
356 fw('fogType="%s"\n' % ('LINEAR' if (mtype
== 'LINEAR') else 'EXPONENTIAL'))
357 fw(ident_step
+ 'color="%.3f %.3f %.3f"\n' % clamp_color(world
.horizon_color
))
358 fw(ident_step
+ 'visibilityRange="%.3f"\n' % mparam
.depth
)
359 fw(ident_step
+ '/>\n')
363 def writeNavigationInfo(ident
, scene
, has_light
):
364 ident_step
= ident
+ (' ' * (-len(ident
) + \
365 fw('%s<NavigationInfo ' % ident
)))
366 fw('headlight="%s"\n' % bool_as_str(not has_light
))
367 fw(ident_step
+ 'visibilityLimit="0.0"\n')
368 fw(ident_step
+ 'type=\'"EXAMINE", "ANY"\'\n')
369 fw(ident_step
+ 'avatarSize="0.25, 1.75, 0.75"\n')
370 fw(ident_step
+ '/>\n')
372 def writeTransform_begin(ident
, matrix
, def_id
):
373 ident_step
= ident
+ (' ' * (-len(ident
) + \
374 fw('%s<Transform ' % ident
)))
375 if def_id
is not None:
376 fw('DEF=%s\n' % def_id
)
380 loc
, rot
, sca
= matrix
.decompose()
381 rot
= rot
.to_axis_angle()
382 rot
= (*rot
[0], rot
[1])
384 fw(ident_step
+ 'translation="%.6f %.6f %.6f"\n' % loc
[:])
385 # fw(ident_step + 'center="%.6f %.6f %.6f"\n' % (0, 0, 0))
386 fw(ident_step
+ 'scale="%.6f %.6f %.6f"\n' % sca
[:])
387 fw(ident_step
+ 'rotation="%.6f %.6f %.6f %.6f"\n' % rot
)
388 fw(ident_step
+ '>\n')
392 def writeTransform_end(ident
):
394 fw('%s</Transform>\n' % ident
)
397 def writeSpotLight(ident
, obj
, matrix
, light
, world
):
398 # note, light_id is not re-used
399 light_id
= quoteattr(unique_name(obj
, LA_
+ obj
.name
, uuid_cache_light
, clean_func
=clean_def
, sep
="_"))
402 ambi
= world
.ambient_color
403 amb_intensity
= ((ambi
[0] + ambi
[1] + ambi
[2]) / 3.0) / 2.5
408 # compute cutoff and beamwidth
409 intensity
= min(lamp
.energy
/ 1.75, 1.0)
410 beamWidth
= lamp
.spot_size
* 0.37
411 # beamWidth=((lamp.spotSize*math.pi)/180.0)*.37
412 cutOffAngle
= beamWidth
* 1.3
414 orientation
= matrix_direction_neg_z(matrix
)
416 location
= matrix
.to_translation()[:]
418 radius
= lamp
.distance
* math
.cos(beamWidth
)
419 # radius = lamp.dist*math.cos(beamWidth)
420 ident_step
= ident
+ (' ' * (-len(ident
) + \
421 fw('%s<SpotLight ' % ident
)))
422 fw('DEF=%s\n' % light_id
)
423 fw(ident_step
+ 'radius="%.4f"\n' % radius
)
424 fw(ident_step
+ 'ambientIntensity="%.4f"\n' % amb_intensity
)
425 fw(ident_step
+ 'intensity="%.4f"\n' % intensity
)
426 fw(ident_step
+ 'color="%.4f %.4f %.4f"\n' % clamp_color(light
.color
))
427 fw(ident_step
+ 'beamWidth="%.4f"\n' % beamWidth
)
428 fw(ident_step
+ 'cutOffAngle="%.4f"\n' % cutOffAngle
)
429 fw(ident_step
+ 'direction="%.4f %.4f %.4f"\n' % orientation
)
430 fw(ident_step
+ 'location="%.4f %.4f %.4f"\n' % location
)
431 fw(ident_step
+ '/>\n')
433 def writeDirectionalLight(ident
, obj
, matrix
, light
, world
):
434 # note, light_id is not re-used
435 light_id
= quoteattr(unique_name(obj
, LA_
+ obj
.name
, uuid_cache_light
, clean_func
=clean_def
, sep
="_"))
438 ambi
= world
.ambient_color
440 amb_intensity
= ((float(ambi
[0] + ambi
[1] + ambi
[2])) / 3.0) / 2.5
445 intensity
= min(light
.energy
/ 1.75, 1.0)
447 orientation
= matrix_direction_neg_z(matrix
)
449 ident_step
= ident
+ (' ' * (-len(ident
) + \
450 fw('%s<DirectionalLight ' % ident
)))
451 fw('DEF=%s\n' % light_id
)
452 fw(ident_step
+ 'ambientIntensity="%.4f"\n' % amb_intensity
)
453 fw(ident_step
+ 'color="%.4f %.4f %.4f"\n' % clamp_color(light
.color
))
454 fw(ident_step
+ 'intensity="%.4f"\n' % intensity
)
455 fw(ident_step
+ 'direction="%.4f %.4f %.4f"\n' % orientation
)
456 fw(ident_step
+ '/>\n')
458 def writePointLight(ident
, obj
, matrix
, light
, world
):
459 # note, light_id is not re-used
460 light_id
= quoteattr(unique_name(obj
, LA_
+ obj
.name
, uuid_cache_light
, clean_func
=clean_def
, sep
="_"))
463 ambi
= world
.ambient_color
465 amb_intensity
= ((float(ambi
[0] + ambi
[1] + ambi
[2])) / 3.0) / 2.5
470 intensity
= min(light
.energy
/ 1.75, 1.0)
471 location
= matrix
.to_translation()[:]
473 ident_step
= ident
+ (' ' * (-len(ident
) + \
474 fw('%s<PointLight ' % ident
)))
475 fw('DEF=%s\n' % light_id
)
476 fw(ident_step
+ 'ambientIntensity="%.4f"\n' % amb_intensity
)
477 fw(ident_step
+ 'color="%.4f %.4f %.4f"\n' % clamp_color(light
.color
))
479 fw(ident_step
+ 'intensity="%.4f"\n' % intensity
)
480 fw(ident_step
+ 'radius="%.4f" \n' % light
.distance
)
481 fw(ident_step
+ 'location="%.4f %.4f %.4f"\n' % location
)
482 fw(ident_step
+ '/>\n')
484 def writeIndexedFaceSet(ident
, obj
, mesh
, mesh_name
, matrix
, world
):
485 obj_id
= quoteattr(unique_name(obj
, OB_
+ obj
.name
, uuid_cache_object
, clean_func
=clean_def
, sep
="_"))
486 mesh_id
= quoteattr(unique_name(mesh
, ME_
+ mesh_name
, uuid_cache_mesh
, clean_func
=clean_def
, sep
="_"))
487 mesh_id_group
= prefix_quoted_str(mesh_id
, group_
)
488 mesh_id_coords
= prefix_quoted_str(mesh_id
, 'coords_')
489 mesh_id_normals
= prefix_quoted_str(mesh_id
, 'normals_')
491 # Be sure tessellated loop triangles are available!
493 if not mesh
.loop_triangles
and mesh
.polygons
:
494 mesh
.calc_loop_triangles()
496 use_collnode
= bool([mod
for mod
in obj
.modifiers
497 if mod
.type == 'COLLISION'
498 if mod
.show_viewport
])
501 fw('%s<Collision enabled="true">\n' % ident
)
504 # use _ifs_TRANSFORM suffix so we dont collide with transform node when
505 # hierarchys are used.
506 ident
= writeTransform_begin(ident
, matrix
, suffix_quoted_str(obj_id
, "_ifs" + _TRANSFORM
))
509 fw('%s<Group USE=%s />\n' % (ident
, mesh_id_group
))
513 fw('%s<Group DEF=%s>\n' % (ident
, mesh_id_group
))
516 is_uv
= bool(mesh
.uv_layers
.active
)
517 # is_col, defined for each material
519 is_coords_written
= False
521 mesh_materials
= mesh
.materials
[:]
522 if not mesh_materials
:
523 mesh_materials
= [None]
525 mesh_material_tex
= [None] * len(mesh_materials
)
526 mesh_material_mtex
= [None] * len(mesh_materials
)
527 mesh_material_images
= [None] * len(mesh_materials
)
529 for i
, material
in enumerate(mesh_materials
):
531 for mtex
in material
.texture_slots
:
534 if tex
and tex
.type == 'IMAGE':
537 mesh_material_tex
[i
] = tex
538 mesh_material_mtex
[i
] = mtex
539 mesh_material_images
[i
] = image
543 mesh_vertices
= mesh
.vertices
[:]
544 mesh_loops
= mesh
.loops
[:]
545 mesh_polygons
= mesh
.polygons
[:]
546 mesh_polygons_materials
= [p
.material_index
for p
in mesh_polygons
]
547 mesh_polygons_vertices
= [p
.vertices
[:] for p
in mesh_polygons
]
549 if len(set(mesh_material_images
)) > 0: # make sure there is at least one image
550 mesh_polygons_image
= [mesh_material_images
[material_index
] for material_index
in mesh_polygons_materials
]
552 mesh_polygons_image
= [None] * len(mesh_polygons
)
553 mesh_polygons_image_unique
= set(mesh_polygons_image
)
557 for material_index
in range(len(mesh_materials
)):
558 for image
in mesh_polygons_image_unique
:
559 polygons_groups
[material_index
, image
] = []
560 del mesh_polygons_image_unique
562 for i
, (material_index
, image
) in enumerate(zip(mesh_polygons_materials
, mesh_polygons_image
)):
563 polygons_groups
[material_index
, image
].append(i
)
565 # Py dict are sorted now, so we can use directly polygons_groups.items()
566 # and still get consistent reproducible outputs.
568 is_col
= mesh
.vertex_colors
.active
569 mesh_loops_col
= mesh
.vertex_colors
.active
.data
if is_col
else None
571 # Check if vertex colors can be exported in per-vertex mode.
572 # Do we have just one color per vertex in every face that uses the vertex?
574 def calc_vertex_color():
575 vert_color
= [None] * len(mesh
.vertices
)
577 for i
, p
in enumerate(mesh_polygons
):
578 for lidx
in p
.loop_indices
:
580 if vert_color
[l
.vertex_index
] is None:
581 vert_color
[l
.vertex_index
] = mesh_loops_col
[lidx
].color
[:]
582 elif vert_color
[l
.vertex_index
] != mesh_loops_col
[lidx
].color
[:]:
585 return True, vert_color
587 is_col_per_vertex
, vert_color
= calc_vertex_color()
588 del calc_vertex_color
590 # If using looptris, we need a mapping poly_index -> loop_tris_indices...
592 polygons_to_loop_triangles_indices
= [[] for i
in range(len(mesh_polygons
))]
593 for ltri
in mesh
.loop_triangles
:
594 polygons_to_loop_triangles_indices
[ltri
.polygon_index
].append(ltri
)
596 for (material_index
, image
), polygons_group
in polygons_groups
.items():
598 material
= mesh_materials
[material_index
]
600 fw('%s<Shape>\n' % ident
)
605 # kludge but as good as it gets!
606 for i
in polygons_group
:
607 if mesh_polygons
[i
].use_smooth
:
611 # UV's and VCols split verts off which effects smoothing
612 # force writing normals in this case.
613 # Also, creaseAngle is not supported for IndexedTriangleSet,
614 # so write normals when is_smooth (otherwise
615 # IndexedTriangleSet can have only all smooth/all flat shading).
616 is_force_normals
= use_triangulate
and (is_smooth
or is_uv
or is_col
)
619 gpu_shader
= gpu_shader_cache
.get(material
) # material can be 'None', uses dummy cache
620 if gpu_shader
is None:
621 gpu_shader
= gpu_shader_cache
[material
] = gpu
.export_shader(scene
, material
)
624 gpu_shader_tmp
= gpu
.export_shader(scene
, material
)
626 print('\nWRITING MATERIAL:', material
.name
)
627 del gpu_shader_tmp
['fragment']
628 del gpu_shader_tmp
['vertex']
629 pprint
.pprint(gpu_shader_tmp
, width
=120)
630 #pprint.pprint(val['vertex'])
633 fw('%s<Appearance>\n' % ident
)
636 if image
and not use_h3d
:
637 writeImageTexture(ident
, image
)
640 loc
= mesh_material_mtex
[material_index
].offset
[:2]
642 # mtex_scale * tex_repeat
643 sca_x
, sca_y
= mesh_material_mtex
[material_index
].scale
[:2]
645 sca_x
*= mesh_material_tex
[material_index
].repeat_x
646 sca_y
*= mesh_material_tex
[material_index
].repeat_y
648 # flip x/y is a sampling feature, convert to transform
649 if mesh_material_tex
[material_index
].use_flip_axis
:
651 sca_x
, sca_y
= sca_y
, -sca_x
655 ident_step
= ident
+ (' ' * (-len(ident
) + \
656 fw('%s<TextureTransform ' % ident
)))
658 # fw('center="%.6f %.6f" ' % (0.0, 0.0))
659 fw(ident_step
+ 'translation="%.6f %.6f"\n' % loc
)
660 fw(ident_step
+ 'scale="%.6f %.6f"\n' % (sca_x
, sca_y
))
661 fw(ident_step
+ 'rotation="%.6f"\n' % rot
)
662 fw(ident_step
+ '/>\n')
665 mat_tmp
= material
if material
else gpu_shader_dummy_mat
666 writeMaterialH3D(ident
, mat_tmp
, world
,
671 writeMaterial(ident
, material
, world
)
674 fw('%s</Appearance>\n' % ident
)
676 mesh_loops_uv
= mesh
.uv_layers
.active
.data
if is_uv
else None
678 #-- IndexedFaceSet or IndexedLineSet
680 ident_step
= ident
+ (' ' * (-len(ident
) + \
681 fw('%s<IndexedTriangleSet ' % ident
)))
683 # --- Write IndexedTriangleSet Attributes (same as IndexedFaceSet)
684 fw('solid="%s"\n' % bool_as_str(material
and material
.use_backface_culling
))
686 if use_normals
or is_force_normals
:
687 fw(ident_step
+ 'normalPerVertex="true"\n')
689 # Tell X3D browser to generate flat (per-face) normals
690 fw(ident_step
+ 'normalPerVertex="false"\n')
694 def _tuple_from_rounded_iter(it
):
695 return tuple(round(v
, 5) for v
in it
)
701 def vertex_key(lidx
):
703 _tuple_from_rounded_iter(mesh_loops_uv
[lidx
].uv
),
704 _tuple_from_rounded_iter(mesh_loops_col
[lidx
].color
),
709 def vertex_key(lidx
):
711 _tuple_from_rounded_iter(mesh_loops_uv
[lidx
].uv
),
716 def vertex_key(lidx
):
718 _tuple_from_rounded_iter(mesh_loops_col
[lidx
].color
),
721 # ack, not especially efficient in this case
722 def vertex_key(lidx
):
725 # build a mesh mapping dict
726 vertex_hash
= [{} for i
in range(len(mesh
.vertices
))]
727 face_tri_list
= [[None, None, None] for i
in range(len(mesh
.loop_triangles
))]
731 temp_tri
= [None] * 3
732 for pidx
in polygons_group
:
733 for ltri
in polygons_to_loop_triangles_indices
[pidx
]:
734 for tri_vidx
, (lidx
, vidx
) in enumerate(zip(ltri
.loops
, ltri
.vertices
)):
735 key
= vertex_key(lidx
)
736 vh
= vertex_hash
[vidx
]
739 x3d_v
= key
, vidx
, totvert
741 # key / original_vertex / new_vertex
742 vert_tri_list
.append(x3d_v
)
744 temp_tri
[tri_vidx
] = x3d_v
746 face_tri_list
[totface
][:] = temp_tri
[:]
750 del _tuple_from_rounded_iter
751 assert(len(face_tri_list
) == len(mesh
.loop_triangles
))
753 fw(ident_step
+ 'index="')
754 for x3d_f
in face_tri_list
:
755 fw('%i %i %i ' % (x3d_f
[0][2], x3d_f
[1][2], x3d_f
[2][2]))
758 # close IndexedTriangleSet
759 fw(ident_step
+ '>\n')
762 fw('%s<Coordinate ' % ident
)
764 for x3d_v
in vert_tri_list
:
765 fw('%.6f %.6f %.6f ' % mesh_vertices
[x3d_v
[1]].co
[:])
768 if use_normals
or is_force_normals
:
769 fw('%s<Normal ' % ident
)
771 for x3d_v
in vert_tri_list
:
772 fw('%.6f %.6f %.6f ' % mesh_vertices
[x3d_v
[1]].normal
[:])
776 fw('%s<TextureCoordinate point="' % ident
)
777 for x3d_v
in vert_tri_list
:
778 fw('%.4f %.4f ' % x3d_v
[0][slot_uv
])
782 fw('%s<ColorRGBA color="' % ident
)
783 for x3d_v
in vert_tri_list
:
784 fw('%.3f %.3f %.3f %.3f ' % x3d_v
[0][slot_col
])
789 for gpu_attr
in gpu_shader
['attributes']:
792 if gpu_attr
['type'] == gpu
.CD_MTFACE
:
793 if gpu_attr
['datatype'] == gpu
.GPU_DATA_2F
:
794 fw('%s<FloatVertexAttribute ' % ident
)
795 fw('name="%s" ' % gpu_attr
['varname'])
796 fw('numComponents="2" ')
798 for x3d_v
in vert_tri_list
:
799 fw('%.4f %.4f ' % x3d_v
[0][slot_uv
])
804 elif gpu_attr
['type'] == gpu
.CD_MCOL
:
805 if gpu_attr
['datatype'] == gpu
.GPU_DATA_4UB
:
806 pass # XXX, H3D can't do
812 fw('%s</IndexedTriangleSet>\n' % ident
)
815 ident_step
= ident
+ (' ' * (-len(ident
) + \
816 fw('%s<IndexedFaceSet ' % ident
)))
818 # --- Write IndexedFaceSet Attributes (same as IndexedTriangleSet)
819 fw('solid="%s"\n' % bool_as_str(material
and material
.use_backface_culling
))
821 # use Auto-Smooth angle, if enabled. Otherwise make
822 # the mesh perfectly smooth by creaseAngle > pi.
823 fw(ident_step
+ 'creaseAngle="%.4f"\n' % (mesh
.auto_smooth_angle
if mesh
.use_auto_smooth
else 4.0))
826 # currently not optional, could be made so:
827 fw(ident_step
+ 'normalPerVertex="true"\n')
829 # IndexedTriangleSet assumes true
830 if is_col
and not is_col_per_vertex
:
831 fw(ident_step
+ 'colorPerVertex="false"\n')
833 # for IndexedTriangleSet we use a uv per vertex so this isn't needed.
835 fw(ident_step
+ 'texCoordIndex="')
838 for i
in polygons_group
:
839 num_poly_verts
= len(mesh_polygons_vertices
[i
])
840 fw('%s -1 ' % ' '.join((str(i
) for i
in range(j
, j
+ num_poly_verts
))))
843 # --- end texCoordIndex
846 fw(ident_step
+ 'coordIndex="')
847 for i
in polygons_group
:
848 poly_verts
= mesh_polygons_vertices
[i
]
849 fw('%s -1 ' % ' '.join((str(i
) for i
in poly_verts
)))
854 # close IndexedFaceSet
855 fw(ident_step
+ '>\n')
858 # --- Write IndexedFaceSet Elements
860 if is_coords_written
:
861 fw('%s<Coordinate USE=%s />\n' % (ident
, mesh_id_coords
))
863 fw('%s<Normal USE=%s />\n' % (ident
, mesh_id_normals
))
865 ident_step
= ident
+ (' ' * (-len(ident
) + \
866 fw('%s<Coordinate ' % ident
)))
867 fw('DEF=%s\n' % mesh_id_coords
)
868 fw(ident_step
+ 'point="')
869 for v
in mesh
.vertices
:
870 fw('%.6f %.6f %.6f ' % v
.co
[:])
872 fw(ident_step
+ '/>\n')
874 is_coords_written
= True
877 ident_step
= ident
+ (' ' * (-len(ident
) + \
878 fw('%s<Normal ' % ident
)))
879 fw('DEF=%s\n' % mesh_id_normals
)
880 fw(ident_step
+ 'vector="')
881 for v
in mesh
.vertices
:
882 fw('%.6f %.6f %.6f ' % v
.normal
[:])
884 fw(ident_step
+ '/>\n')
887 fw('%s<TextureCoordinate point="' % ident
)
888 for i
in polygons_group
:
889 for lidx
in mesh_polygons
[i
].loop_indices
:
890 fw('%.4f %.4f ' % mesh_loops_uv
[lidx
].uv
[:])
894 # Need better logic here, dynamic determination
895 # which of the X3D coloring models fits better this mesh - per face
896 # or per vertex. Probably with an explicit fallback mode parameter.
897 fw('%s<ColorRGBA color="' % ident
)
898 if is_col_per_vertex
:
899 for i
in range(len(mesh
.vertices
)):
901 fw('%.3f %.3f %.3f %.3f ' % (vert_color
[i
] or (0.0, 0.0, 0.0, 0.0)))
902 else: # Export as colors per face.
903 # TODO: average them rather than using the first one!
904 for i
in polygons_group
:
905 fw('%.3f %.3f %.3f %.3f ' % mesh_loops_col
[mesh_polygons
[i
].loop_start
].color
[:])
908 #--- output vertexColors
910 #--- output closing braces
913 fw('%s</IndexedFaceSet>\n' % ident
)
916 fw('%s</Shape>\n' % ident
)
920 #fw('%s<PythonScript DEF="PS" url="object.py" >\n' % ident)
921 #fw('%s <ShaderProgram USE="MA_Material.005" containerField="references"/>\n' % ident)
922 #fw('%s</PythonScript>\n' % ident)
925 fw('%s</Group>\n' % ident
)
927 ident
= writeTransform_end(ident
)
931 fw('%s</Collision>\n' % ident
)
933 def writeMaterial(ident
, material
, world
):
934 material_id
= quoteattr(unique_name(material
, MA_
+ material
.name
, uuid_cache_material
, clean_func
=clean_def
, sep
="_"))
936 # look up material name, use it if available
938 fw('%s<Material USE=%s />\n' % (ident
, material_id
))
942 emit
= 0.0 #material.emit
943 ambient
= 0.0 #material.ambient / 3.0
944 diffuseColor
= material
.diffuse_color
[:3]
946 ambiColor
= ((material
.ambient
* 2.0) * world
.ambient_color
)[:]
948 ambiColor
= 0.0, 0.0, 0.0
950 emitColor
= tuple(((c
* emit
) + ambiColor
[i
]) / 2.0 for i
, c
in enumerate(diffuseColor
))
951 shininess
= material
.specular_intensity
952 specColor
= tuple((c
+ 0.001) / (1.25 / (material
.specular_intensity
+ 0.001)) for c
in material
.specular_color
)
953 transp
= 1.0 - material
.diffuse_color
[3]
955 # ~ if material.use_shadeless:
958 # ~ specColor = emitColor = diffuseColor
960 ident_step
= ident
+ (' ' * (-len(ident
) + \
961 fw('%s<Material ' % ident
)))
962 fw('DEF=%s\n' % material_id
)
963 fw(ident_step
+ 'diffuseColor="%.3f %.3f %.3f"\n' % clamp_color(diffuseColor
))
964 fw(ident_step
+ 'specularColor="%.3f %.3f %.3f"\n' % clamp_color(specColor
))
965 fw(ident_step
+ 'emissiveColor="%.3f %.3f %.3f"\n' % clamp_color(emitColor
))
966 fw(ident_step
+ 'ambientIntensity="%.3f"\n' % ambient
)
967 fw(ident_step
+ 'shininess="%.3f"\n' % shininess
)
968 fw(ident_step
+ 'transparency="%s"\n' % transp
)
969 fw(ident_step
+ '/>\n')
971 def writeMaterialH3D(ident
, material
, world
,
973 material_id
= quoteattr(unique_name(material
, 'MA_' + material
.name
, uuid_cache_material
, clean_func
=clean_def
, sep
="_"))
975 fw('%s<Material />\n' % ident
)
977 fw('%s<ComposedShader USE=%s />\n' % (ident
, material_id
))
981 # GPU_material_bind_uniforms
982 # GPU_begin_object_materials
996 #~ GPU_DYNAMIC_LIGHT_DYNCO 7
997 #~ GPU_DYNAMIC_LIGHT_DYNCOL 11
998 #~ GPU_DYNAMIC_LIGHT_DYNENERGY 10
999 #~ GPU_DYNAMIC_LIGHT_DYNIMAT 8
1000 #~ GPU_DYNAMIC_LIGHT_DYNPERSMAT 9
1001 #~ GPU_DYNAMIC_LIGHT_DYNVEC 6
1002 #~ GPU_DYNAMIC_OBJECT_COLOR 5
1003 #~ GPU_DYNAMIC_OBJECT_IMAT 4
1004 #~ GPU_DYNAMIC_OBJECT_MAT 2
1005 #~ GPU_DYNAMIC_OBJECT_VIEWIMAT 3
1006 #~ GPU_DYNAMIC_OBJECT_VIEWMAT 1
1007 #~ GPU_DYNAMIC_SAMPLER_2DBUFFER 12
1008 #~ GPU_DYNAMIC_SAMPLER_2DIMAGE 13
1009 #~ GPU_DYNAMIC_SAMPLER_2DSHADOW 14
1012 inline const char* typeToString( X3DType t ) {
1014 case SFFLOAT: return "SFFloat";
1015 case MFFLOAT: return "MFFloat";
1016 case SFDOUBLE: return "SFDouble";
1017 case MFDOUBLE: return "MFDouble";
1018 case SFTIME: return "SFTime";
1019 case MFTIME: return "MFTime";
1020 case SFINT32: return "SFInt32";
1021 case MFINT32: return "MFInt32";
1022 case SFVEC2F: return "SFVec2f";
1023 case MFVEC2F: return "MFVec2f";
1024 case SFVEC2D: return "SFVec2d";
1025 case MFVEC2D: return "MFVec2d";
1026 case SFVEC3F: return "SFVec3f";
1027 case MFVEC3F: return "MFVec3f";
1028 case SFVEC3D: return "SFVec3d";
1029 case MFVEC3D: return "MFVec3d";
1030 case SFVEC4F: return "SFVec4f";
1031 case MFVEC4F: return "MFVec4f";
1032 case SFVEC4D: return "SFVec4d";
1033 case MFVEC4D: return "MFVec4d";
1034 case SFBOOL: return "SFBool";
1035 case MFBOOL: return "MFBool";
1036 case SFSTRING: return "SFString";
1037 case MFSTRING: return "MFString";
1038 case SFNODE: return "SFNode";
1039 case MFNODE: return "MFNode";
1040 case SFCOLOR: return "SFColor";
1041 case MFCOLOR: return "MFColor";
1042 case SFCOLORRGBA: return "SFColorRGBA";
1043 case MFCOLORRGBA: return "MFColorRGBA";
1044 case SFROTATION: return "SFRotation";
1045 case MFROTATION: return "MFRotation";
1046 case SFQUATERNION: return "SFQuaternion";
1047 case MFQUATERNION: return "MFQuaternion";
1048 case SFMATRIX3F: return "SFMatrix3f";
1049 case MFMATRIX3F: return "MFMatrix3f";
1050 case SFMATRIX4F: return "SFMatrix4f";
1051 case MFMATRIX4F: return "MFMatrix4f";
1052 case SFMATRIX3D: return "SFMatrix3d";
1053 case MFMATRIX3D: return "MFMatrix3d";
1054 case SFMATRIX4D: return "SFMatrix4d";
1055 case MFMATRIX4D: return "MFMatrix4d";
1056 case UNKNOWN_X3D_TYPE:
1057 default:return "UNKNOWN_X3D_TYPE";
1061 fw('%s<ComposedShader DEF=%s language="GLSL" >\n' % (ident
, material_id
))
1064 shader_url_frag
= 'shaders/%s_%s.frag' % (filename_strip
, material_id
[1:-1])
1065 shader_url_vert
= 'shaders/%s_%s.vert' % (filename_strip
, material_id
[1:-1])
1068 shader_dir
= os
.path
.join(base_dst
, 'shaders')
1069 if not os
.path
.isdir(shader_dir
):
1070 os
.mkdir(shader_dir
)
1072 # ------------------------------------------------------
1074 field_descr
= " <!--- H3D View Matrix Patch -->"
1075 fw('%s<field name="%s" type="SFMatrix4f" accessType="inputOutput" />%s\n' % (ident
, H3D_VIEW_MATRIX
, field_descr
))
1076 frag_vars
= ["uniform mat4 %s;" % H3D_VIEW_MATRIX
]
1078 # annoying!, we need to track if some of the directional lamp
1079 # vars are children of the camera or not, since this adjusts how
1081 frag_uniform_var_map
= {}
1083 h3d_material_route
.append(
1084 '<ROUTE fromNode="%s" fromField="glModelViewMatrix" toNode=%s toField="%s" />%s' %
1085 (H3D_TOP_LEVEL
, material_id
, H3D_VIEW_MATRIX
, field_descr
))
1086 # ------------------------------------------------------
1088 for uniform
in gpu_shader
['uniforms']:
1089 if uniform
['type'] == gpu
.GPU_DYNAMIC_SAMPLER_2DIMAGE
:
1090 field_descr
= " <!--- Dynamic Sampler 2d Image -->"
1091 fw('%s<field name="%s" type="SFNode" accessType="inputOutput">%s\n' % (ident
, uniform
['varname'], field_descr
))
1092 writeImageTexture(ident
+ '\t', uniform
['image'])
1093 fw('%s</field>\n' % ident
)
1095 elif uniform
['type'] == gpu
.GPU_DYNAMIC_LIGHT_DYNCO
:
1096 light_obj
= uniform
['lamp']
1097 frag_uniform_var_map
[uniform
['varname']] = light_obj
1099 if uniform
['datatype'] == gpu
.GPU_DATA_3F
: # should always be true!
1100 light_obj_id
= quoteattr(unique_name(light_obj
, LA_
+ light_obj
.name
, uuid_cache_light
, clean_func
=clean_def
, sep
="_"))
1101 light_obj_transform_id
= quoteattr(unique_name(light_obj
, light_obj
.name
, uuid_cache_object
, clean_func
=clean_def
, sep
="_"))
1103 value
= '%.6f %.6f %.6f' % (global_matrix
* light_obj
.matrix_world
).to_translation()[:]
1104 field_descr
= " <!--- Lamp DynCo '%s' -->" % light_obj
.name
1105 fw('%s<field name="%s" type="SFVec3f" accessType="inputOutput" value="%s" />%s\n' % (ident
, uniform
['varname'], value
, field_descr
))
1107 # ------------------------------------------------------
1109 field_descr
= " <!--- Lamp DynCo '%s' (shader patch) -->" % light_obj
.name
1110 fw('%s<field name="%s_transform" type="SFMatrix4f" accessType="inputOutput" />%s\n' % (ident
, uniform
['varname'], field_descr
))
1113 frag_vars
.append("uniform mat4 %s_transform;" % uniform
['varname'])
1114 h3d_material_route
.append(
1115 '<ROUTE fromNode=%s fromField="accumulatedForward" toNode=%s toField="%s_transform" />%s' %
1116 (suffix_quoted_str(light_obj_transform_id
, _TRANSFORM
), material_id
, uniform
['varname'], field_descr
))
1118 h3d_material_route
.append(
1119 '<ROUTE fromNode=%s fromField="location" toNode=%s toField="%s" /> %s' %
1120 (light_obj_id
, material_id
, uniform
['varname'], field_descr
))
1121 # ------------------------------------------------------
1126 elif uniform
['type'] == gpu
.GPU_DYNAMIC_LIGHT_DYNCOL
:
1127 # odd we have both 3, 4 types.
1128 light_obj
= uniform
['lamp']
1129 frag_uniform_var_map
[uniform
['varname']] = light_obj
1131 lamp
= light_obj
.data
1132 value
= '%.6f %.6f %.6f' % (lamp
.color
* lamp
.energy
)[:]
1133 field_descr
= " <!--- Lamp DynColor '%s' -->" % light_obj
.name
1134 if uniform
['datatype'] == gpu
.GPU_DATA_3F
:
1135 fw('%s<field name="%s" type="SFVec3f" accessType="inputOutput" value="%s" />%s\n' % (ident
, uniform
['varname'], value
, field_descr
))
1136 elif uniform
['datatype'] == gpu
.GPU_DATA_4F
:
1137 fw('%s<field name="%s" type="SFVec4f" accessType="inputOutput" value="%s 1.0" />%s\n' % (ident
, uniform
['varname'], value
, field_descr
))
1141 elif uniform
['type'] == gpu
.GPU_DYNAMIC_LIGHT_DYNENERGY
:
1145 elif uniform
['type'] == gpu
.GPU_DYNAMIC_LIGHT_DYNVEC
:
1146 light_obj
= uniform
['lamp']
1147 frag_uniform_var_map
[uniform
['varname']] = light_obj
1149 if uniform
['datatype'] == gpu
.GPU_DATA_3F
:
1150 light_obj
= uniform
['lamp']
1151 value
= '%.6f %.6f %.6f' % ((global_matrix
* light_obj
.matrix_world
).to_quaternion() * mathutils
.Vector((0.0, 0.0, 1.0))).normalized()[:]
1152 field_descr
= " <!--- Lamp DynDirection '%s' -->" % light_obj
.name
1153 fw('%s<field name="%s" type="SFVec3f" accessType="inputOutput" value="%s" />%s\n' % (ident
, uniform
['varname'], value
, field_descr
))
1155 # route so we can have the lamp update the view
1156 if h3d_is_object_view(scene
, light_obj
):
1157 light_id
= quoteattr(unique_name(light_obj
, LA_
+ light_obj
.name
, uuid_cache_light
, clean_func
=clean_def
, sep
="_"))
1158 h3d_material_route
.append(
1159 '<ROUTE fromNode=%s fromField="direction" toNode=%s toField="%s" />%s' %
1160 (light_id
, material_id
, uniform
['varname'], field_descr
))
1165 elif uniform
['type'] == gpu
.GPU_DYNAMIC_OBJECT_VIEWIMAT
:
1166 frag_uniform_var_map
[uniform
['varname']] = None
1167 if uniform
['datatype'] == gpu
.GPU_DATA_16F
:
1168 field_descr
= " <!--- Object View Matrix Inverse '%s' -->" % obj
.name
1169 fw('%s<field name="%s" type="SFMatrix4f" accessType="inputOutput" />%s\n' % (ident
, uniform
['varname'], field_descr
))
1171 h3d_material_route
.append(
1172 '<ROUTE fromNode="%s" fromField="glModelViewMatrixInverse" toNode=%s toField="%s" />%s' %
1173 (H3D_TOP_LEVEL
, material_id
, uniform
['varname'], field_descr
))
1177 elif uniform
['type'] == gpu
.GPU_DYNAMIC_OBJECT_IMAT
:
1178 frag_uniform_var_map
[uniform
['varname']] = None
1179 if uniform
['datatype'] == gpu
.GPU_DATA_16F
:
1180 value
= ' '.join(['%.6f' % f
for v
in (global_matrix
* obj
.matrix_world
).inverted().transposed() for f
in v
])
1181 field_descr
= " <!--- Object Invertex Matrix '%s' -->" % obj
.name
1182 fw('%s<field name="%s" type="SFMatrix4f" accessType="inputOutput" value="%s" />%s\n' % (ident
, uniform
['varname'], value
, field_descr
))
1186 elif uniform
['type'] == gpu
.GPU_DYNAMIC_SAMPLER_2DSHADOW
:
1187 pass # XXX, shadow buffers not supported.
1189 elif uniform
['type'] == gpu
.GPU_DYNAMIC_SAMPLER_2DBUFFER
:
1190 frag_uniform_var_map
[uniform
['varname']] = None
1192 if uniform
['datatype'] == gpu
.GPU_DATA_1I
:
1194 tex
= uniform
['texpixels']
1196 for i
in range(0, len(tex
) - 1, 4):
1198 value
.append('0x%.2x%.2x%.2x%.2x' % (col
[0], col
[1], col
[2], col
[3]))
1200 field_descr
= " <!--- Material Buffer -->"
1201 fw('%s<field name="%s" type="SFNode" accessType="inputOutput">%s\n' % (ident
, uniform
['varname'], field_descr
))
1205 ident_step
= ident
+ (' ' * (-len(ident
) + \
1206 fw('%s<PixelTexture \n' % ident
)))
1207 fw(ident_step
+ 'repeatS="false"\n')
1208 fw(ident_step
+ 'repeatT="false"\n')
1210 fw(ident_step
+ 'image="%s 1 4 %s"\n' % (len(value
), " ".join(value
)))
1212 fw(ident_step
+ '/>\n')
1216 fw('%s</field>\n' % ident
)
1218 #for i in range(0, 10, 4)
1219 #value = ' '.join(['%d' % f for f in uniform['texpixels']])
1220 # value = ' '.join(['%.6f' % (f / 256) for f in uniform['texpixels']])
1222 #fw('%s<field name="%s" type="SFInt32" accessType="inputOutput" value="%s" />%s\n' % (ident, uniform['varname'], value, field_descr))
1223 #print('test', len(uniform['texpixels']))
1227 print("SKIPPING", uniform
['type'])
1229 file_frag
= open(os
.path
.join(base_dst
, shader_url_frag
), 'w', encoding
='utf-8')
1230 file_frag
.write(gpu_shader
['fragment'])
1233 h3d_shader_glsl_frag_patch(os
.path
.join(base_dst
, shader_url_frag
),
1236 frag_uniform_var_map
,
1239 file_vert
= open(os
.path
.join(base_dst
, shader_url_vert
), 'w', encoding
='utf-8')
1240 file_vert
.write(gpu_shader
['vertex'])
1243 fw('%s<ShaderPart type="FRAGMENT" url=%s />\n' % (ident
, quoteattr(shader_url_frag
)))
1244 fw('%s<ShaderPart type="VERTEX" url=%s />\n' % (ident
, quoteattr(shader_url_vert
)))
1247 fw('%s</ComposedShader>\n' % ident
)
1249 def writeImageTexture(ident
, image
):
1250 image_id
= quoteattr(unique_name(image
, IM_
+ image
.name
, uuid_cache_image
, clean_func
=clean_def
, sep
="_"))
1253 fw('%s<ImageTexture USE=%s />\n' % (ident
, image_id
))
1257 ident_step
= ident
+ (' ' * (-len(ident
) + \
1258 fw('%s<ImageTexture ' % ident
)))
1259 fw('DEF=%s\n' % image_id
)
1261 # collect image paths, can load multiple
1262 # [relative, name-only, absolute]
1263 filepath
= image
.filepath
1264 filepath_full
= bpy
.path
.abspath(filepath
, library
=image
.library
)
1265 filepath_ref
= bpy_extras
.io_utils
.path_reference(filepath_full
, base_src
, base_dst
, path_mode
, "textures", copy_set
, image
.library
)
1266 filepath_base
= os
.path
.basename(filepath_full
)
1272 if path_mode
!= 'RELATIVE':
1273 images
.append(filepath_full
)
1275 images
= [f
.replace('\\', '/') for f
in images
]
1276 images
= [f
for i
, f
in enumerate(images
) if f
not in images
[:i
]]
1278 fw(ident_step
+ "url='%s'\n" % ' '.join(['"%s"' % escape(f
) for f
in images
]))
1279 fw(ident_step
+ '/>\n')
1281 def writeBackground(ident
, world
):
1287 world_id
= quoteattr(unique_name(world
, WO_
+ world
.name
, uuid_cache_world
, clean_func
=clean_def
, sep
="_"))
1289 # XXX World changed a lot in 2.8... For now do minimal get-it-to-work job.
1290 # ~ blending = world.use_sky_blend, world.use_sky_paper, world.use_sky_real
1292 # ~ grd_triple = clamp_color(world.horizon_color)
1293 # ~ sky_triple = clamp_color(world.zenith_color)
1294 # ~ mix_triple = clamp_color((grd_triple[i] + sky_triple[i]) / 2.0 for i in range(3))
1296 blending
= (False, False, False)
1298 grd_triple
= clamp_color(world
.color
)
1299 sky_triple
= clamp_color(world
.color
)
1300 mix_triple
= clamp_color((grd_triple
[i
] + sky_triple
[i
]) / 2.0 for i
in range(3))
1302 ident_step
= ident
+ (' ' * (-len(ident
) + \
1303 fw('%s<Background ' % ident
)))
1304 fw('DEF=%s\n' % world_id
)
1305 # No Skytype - just Hor color
1306 if blending
== (False, False, False):
1307 fw(ident_step
+ 'groundColor="%.3f %.3f %.3f"\n' % grd_triple
)
1308 fw(ident_step
+ 'skyColor="%.3f %.3f %.3f"\n' % grd_triple
)
1310 elif blending
== (True, False, False):
1311 fw(ident_step
+ 'groundColor="%.3f %.3f %.3f, %.3f %.3f %.3f"\n' % (grd_triple
+ mix_triple
))
1312 fw(ident_step
+ 'groundAngle="1.57"\n')
1313 fw(ident_step
+ 'skyColor="%.3f %.3f %.3f, %.3f %.3f %.3f"\n' % (sky_triple
+ mix_triple
))
1314 fw(ident_step
+ 'skyAngle="1.57"\n')
1315 # Blend+Real Gradient Inverse
1316 elif blending
== (True, False, True):
1317 fw(ident_step
+ 'groundColor="%.3f %.3f %.3f, %.3f %.3f %.3f"\n' % (sky_triple
+ grd_triple
))
1318 fw(ident_step
+ 'groundAngle="1.57"\n')
1319 fw(ident_step
+ 'skyColor="%.3f %.3f %.3f, %.3f %.3f %.3f, %.3f %.3f %.3f"\n' % (sky_triple
+ grd_triple
+ sky_triple
))
1320 fw(ident_step
+ 'skyAngle="1.57, 3.14159"\n')
1321 # Paper - just Zen Color
1322 elif blending
== (False, False, True):
1323 fw(ident_step
+ 'groundColor="%.3f %.3f %.3f"\n' % sky_triple
)
1324 fw(ident_step
+ 'skyColor="%.3f %.3f %.3f"\n' % sky_triple
)
1325 # Blend+Real+Paper - komplex gradient
1326 elif blending
== (True, True, True):
1327 fw(ident_step
+ 'groundColor="%.3f %.3f %.3f, %.3f %.3f %.3f"\n' % (sky_triple
+ grd_triple
))
1328 fw(ident_step
+ 'groundAngle="1.57"\n')
1329 fw(ident_step
+ 'skyColor="%.3f %.3f %.3f, %.3f %.3f %.3f"\n' % (sky_triple
+ grd_triple
))
1330 fw(ident_step
+ 'skyAngle="1.57"\n')
1331 # Any Other two colors
1333 fw(ident_step
+ 'groundColor="%.3f %.3f %.3f"\n' % grd_triple
)
1334 fw(ident_step
+ 'skyColor="%.3f %.3f %.3f"\n' % sky_triple
)
1336 for tex
in bpy
.data
.textures
:
1337 if tex
.type == 'IMAGE' and tex
.image
:
1340 basename
= quoteattr(bpy
.path
.basename(pic
.filepath
))
1342 if namemat
== 'back':
1343 fw(ident_step
+ 'backUrl=%s\n' % basename
)
1344 elif namemat
== 'bottom':
1345 fw(ident_step
+ 'bottomUrl=%s\n' % basename
)
1346 elif namemat
== 'front':
1347 fw(ident_step
+ 'frontUrl=%s\n' % basename
)
1348 elif namemat
== 'left':
1349 fw(ident_step
+ 'leftUrl=%s\n' % basename
)
1350 elif namemat
== 'right':
1351 fw(ident_step
+ 'rightUrl=%s\n' % basename
)
1352 elif namemat
== 'top':
1353 fw(ident_step
+ 'topUrl=%s\n' % basename
)
1355 fw(ident_step
+ '/>\n')
1357 # -------------------------------------------------------------------------
1358 # Export Object Hierarchy (recursively called)
1359 # -------------------------------------------------------------------------
1360 def export_object(ident
, obj_main_parent
, obj_main
, obj_children
):
1361 matrix_fallback
= mathutils
.Matrix()
1363 derived_dict
= create_derived_objects(depsgraph
, [obj_main
])
1364 derived
= derived_dict
.get(obj_main
)
1367 obj_main_matrix_world
= obj_main
.matrix_world
1369 obj_main_matrix
= obj_main_parent
.matrix_world
.inverted(matrix_fallback
) @ obj_main_matrix_world
1371 obj_main_matrix
= obj_main_matrix_world
1372 obj_main_matrix_world_invert
= obj_main_matrix_world
.inverted(matrix_fallback
)
1374 obj_main_id
= quoteattr(unique_name(obj_main
, obj_main
.name
, uuid_cache_object
, clean_func
=clean_def
, sep
="_"))
1376 ident
= writeTransform_begin(ident
, obj_main_matrix
if obj_main_parent
else global_matrix
@ obj_main_matrix
, suffix_quoted_str(obj_main_id
, _TRANSFORM
))
1378 # Set here just incase we dont enter the loop below.
1381 for obj
, obj_matrix
in (() if derived
is None else derived
):
1385 # make transform node relative
1386 obj_matrix
= obj_main_matrix_world_invert
@ obj_matrix
1388 obj_matrix
= global_matrix
@ obj_matrix
1390 # H3D - use for writing a dummy transform parent
1393 if obj_type
== 'CAMERA':
1394 writeViewpoint(ident
, obj
, obj_matrix
, scene
)
1396 if use_h3d
and scene
.camera
== obj
:
1397 view_id
= uuid_cache_view
[obj
]
1398 fw('%s<Transform DEF="%s">\n' % (ident
, H3D_CAMERA_FOLLOW
))
1399 h3d_material_route
.extend([
1400 '<ROUTE fromNode="%s" fromField="totalPosition" toNode="%s" toField="translation" />' % (view_id
, H3D_CAMERA_FOLLOW
),
1401 '<ROUTE fromNode="%s" fromField="totalOrientation" toNode="%s" toField="rotation" />' % (view_id
, H3D_CAMERA_FOLLOW
),
1406 elif obj_type
in {'MESH', 'CURVE', 'SURFACE', 'FONT'}:
1407 if (obj_type
!= 'MESH') or (use_mesh_modifiers
and obj
.is_modified(scene
, 'PREVIEW')):
1408 obj_for_mesh
= obj
.evaluated_get(depsgraph
) if use_mesh_modifiers
else obj
1410 me
= obj_for_mesh
.to_mesh()
1419 # ensure unique name, we could also do this by
1420 # postponing mesh removal, but clearing data - TODO
1422 me_name_new
= me_name_original
= obj
.name
.rstrip("1234567890").rstrip(".")
1424 while me_name_new
in mesh_name_set
:
1425 me_name_new
= "%.17s.%03d" % (me_name_original
, count
)
1427 mesh_name_set
.add(me_name_new
)
1428 mesh_name
= me_name_new
1429 del me_name_new
, me_name_original
, count
1434 writeIndexedFaceSet(ident
, obj
, me
, mesh_name
, obj_matrix
, world
)
1436 # free mesh created with to_mesh()
1438 obj_for_mesh
.to_mesh_clear()
1440 elif obj_type
== 'LIGHT':
1442 datatype
= data
.type
1443 if datatype
== 'POINT':
1444 writePointLight(ident
, obj
, obj_matrix
, data
, world
)
1445 elif datatype
== 'SPOT':
1446 writeSpotLight(ident
, obj
, obj_matrix
, data
, world
)
1447 elif datatype
== 'SUN':
1448 writeDirectionalLight(ident
, obj
, obj_matrix
, data
, world
)
1450 writeDirectionalLight(ident
, obj
, obj_matrix
, data
, world
)
1452 #print "Info: Ignoring [%s], object type [%s] not handle yet" % (object.name,object.getType)
1455 # ---------------------------------------------------------------------
1456 # write out children recursively
1457 # ---------------------------------------------------------------------
1458 for obj_child
, obj_child_children
in obj_children
:
1459 export_object(ident
, obj_main
, obj_child
, obj_child_children
)
1463 fw('%s</Transform>\n' % ident
)
1467 ident
= writeTransform_end(ident
)
1469 # -------------------------------------------------------------------------
1470 # Main Export Function
1471 # -------------------------------------------------------------------------
1475 # tag un-exported IDs
1476 bpy
.data
.meshes
.tag(False)
1477 bpy
.data
.materials
.tag(False)
1478 bpy
.data
.images
.tag(False)
1481 objects
= [obj
for obj
in view_layer
.objects
if obj
.visible_get(view_layer
=view_layer
)
1482 and obj
.select_get(view_layer
=view_layer
)]
1484 objects
= [obj
for obj
in view_layer
.objects
if obj
.visible_get(view_layer
=view_layer
)]
1486 print('Info: starting X3D export to %r...' % file.name
)
1488 ident
= writeHeader(ident
)
1490 writeNavigationInfo(ident
, scene
, any(obj
.type == 'LIGHT' for obj
in objects
))
1491 writeBackground(ident
, world
)
1492 writeFog(ident
, world
)
1497 objects_hierarchy
= build_hierarchy(objects
)
1499 objects_hierarchy
= ((obj
, []) for obj
in objects
)
1501 for obj_main
, obj_main_children
in objects_hierarchy
:
1502 export_object(ident
, None, obj_main
, obj_main_children
)
1504 ident
= writeFooter(ident
)
1508 # -------------------------------------------------------------------------
1510 # -------------------------------------------------------------------------
1514 bpy
.data
.materials
.remove(gpu_shader_dummy_mat
)
1516 # copy all collected files.
1518 bpy_extras
.io_utils
.path_reference_copy(copy_set
)
1520 print('Info: finished X3D export to %r' % file.name
)
1523 ##########################################################
1524 # Callbacks, needed before Main
1525 ##########################################################
1528 def gzip_open_utf8(filepath
, mode
):
1529 """Workaround for py3k only allowing binary gzip writing"""
1533 # need to investigate encoding
1534 file = gzip
.open(filepath
, mode
)
1535 write_real
= file.write
1537 def write_wrap(data
):
1538 return write_real(data
.encode("utf-8"))
1540 file.write
= write_wrap
1549 use_mesh_modifiers
=False,
1550 use_triangulate
=False,
1557 name_decorations
=True
1560 bpy
.path
.ensure_ext(filepath
, '.x3dz' if use_compress
else '.x3d')
1562 if bpy
.ops
.object.mode_set
.poll():
1563 bpy
.ops
.object.mode_set(mode
='OBJECT')
1566 file = gzip_open_utf8(filepath
, 'w')
1568 file = open(filepath
, 'w', encoding
='utf-8')
1570 if global_matrix
is None:
1571 global_matrix
= mathutils
.Matrix()
1575 context
.evaluated_depsgraph_get(),
1578 use_mesh_modifiers
=use_mesh_modifiers
,
1579 use_selection
=use_selection
,
1580 use_triangulate
=use_triangulate
,
1581 use_normals
=use_normals
,
1582 use_hierarchy
=use_hierarchy
,
1584 path_mode
=path_mode
,
1585 name_decorations
=name_decorations
,