Remove win2k check
[blender-addons.git] / io_scene_x3d / export_x3d.py
blob24bfbe1e1ce1d72009fffe7618bd2133306f388c
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 #####
19 # <pep8 compliant>
21 # Contributors: bart:neeneenee*de, http://www.neeneenee.de/vrml, Campbell Barton
23 """
24 This script exports to X3D format.
26 Usage:
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.
30 Known issues:
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;
34 """
36 import math
37 import os
39 import bpy
40 import mathutils
42 from bpy_extras.io_utils import create_derived_objects, free_derived_objects
45 # h3d defines
46 H3D_TOP_LEVEL = 'TOP_LEVEL_TI'
47 H3D_CAMERA_FOLLOW = 'CAMERA_FOLLOW_TRANSFORM'
48 H3D_VIEW_MATRIX = 'view_matrix'
51 def clamp_color(col):
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)]
71 def clean_def(txt):
72 # see report [#28256]
73 if not txt:
74 txt = "None"
75 # no digit start
76 if txt[0] in "1234567890+-":
77 txt = "_" + txt
78 return txt.translate({
79 # control characters 0x0-0x1f
80 # 0x00: "_",
81 0x01: "_",
82 0x02: "_",
83 0x03: "_",
84 0x04: "_",
85 0x05: "_",
86 0x06: "_",
87 0x07: "_",
88 0x08: "_",
89 0x09: "_",
90 0x0a: "_",
91 0x0b: "_",
92 0x0c: "_",
93 0x0d: "_",
94 0x0e: "_",
95 0x0f: "_",
96 0x10: "_",
97 0x11: "_",
98 0x12: "_",
99 0x13: "_",
100 0x14: "_",
101 0x15: "_",
102 0x16: "_",
103 0x17: "_",
104 0x18: "_",
105 0x19: "_",
106 0x1a: "_",
107 0x1b: "_",
108 0x1c: "_",
109 0x1d: "_",
110 0x1e: "_",
111 0x1f: "_",
113 0x7f: "_", # 127
115 0x20: "_", # space
116 0x22: "_", # "
117 0x27: "_", # '
118 0x23: "_", # #
119 0x2c: "_", # ,
120 0x2e: "_", # .
121 0x5b: "_", # [
122 0x5d: "_", # ]
123 0x5c: "_", # \
124 0x7b: "_", # {
125 0x7d: "_", # }
129 def build_hierarchy(objects):
130 """ returns parent child relationships, skipping
132 objects_set = set(objects)
133 par_lookup = {}
135 def test_parent(parent):
136 while (parent is not None) and (parent not in objects_set):
137 parent = parent.parent
138 return parent
140 for obj in objects:
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 # -----------------------------------------------------------------------------
151 # H3D Functions
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')
155 lines = []
157 last_transform = None
159 for l in h3d_file:
160 if l.startswith("void main(void)"):
161 lines.append("\n")
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")
166 lines.append("\n")
167 elif l.lstrip().startswith("lamp_visibility_other("):
168 w = l.split(', ')
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])
171 l = ", ".join(w)
172 elif l.lstrip().startswith("lamp_visibility_sun_hemi("):
173 w = l.split(', ')
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]
178 else:
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("):
184 w = l.split(', ')
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]
189 else:
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)
195 lines.append(l)
197 h3d_file.close()
199 h3d_file = open(filepath, 'w', encoding='utf-8')
200 h3d_file.writelines(lines)
201 h3d_file.close()
204 def h3d_is_object_view(scene, obj):
205 camera = scene.camera
206 parent = obj.parent
207 while parent:
208 if parent == camera:
209 return True
210 parent = parent.parent
211 return False
214 # -----------------------------------------------------------------------------
215 # Functions for writing output file
216 # -----------------------------------------------------------------------------
218 def export(file,
219 global_matrix,
220 scene,
221 use_mesh_modifiers=False,
222 use_selection=True,
223 use_triangulate=False,
224 use_normals=False,
225 use_hierarchy=True,
226 use_h3d=False,
227 path_mode='AUTO',
228 name_decorations=True,
231 # -------------------------------------------------------------------------
232 # Global Setup
233 # -------------------------------------------------------------------------
234 import bpy_extras
235 from bpy_extras.io_utils import unique_name
236 from xml.sax.saxutils import quoteattr, escape
238 if name_decorations:
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
250 CA_ = 'CA_'
251 OB_ = 'OB_'
252 ME_ = 'ME_'
253 IM_ = 'IM_'
254 WO_ = 'WO_'
255 MA_ = 'MA_'
256 LA_ = 'LA_'
257 group_ = 'group_'
258 else:
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.
262 uuid_cache = {}
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
270 del uuid_cache
271 CA_ = ''
272 OB_ = ''
273 ME_ = ''
274 IM_ = ''
275 WO_ = ''
276 MA_ = ''
277 LA_ = ''
278 group_ = ''
280 _TRANSFORM = '_TRANSFORM'
282 # store files to copy
283 copy_set = set()
285 # store names of newly cerated meshes, so we dont overlap
286 mesh_name_set = set()
288 fw = file.write
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 = {}
294 if use_h3d:
295 import gpu
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)
309 if use_h3d:
310 fw('%s<X3D profile="H3DAPI" version="1.4">\n' % ident)
311 else:
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)
315 ident += '\t'
316 fw('%s<head>\n' % ident)
317 ident += '\t'
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)
322 ident = ident[:-1]
323 fw('%s</head>\n' % ident)
324 fw('%s<Scene>\n' % ident)
325 ident += '\t'
327 if use_h3d:
328 # outputs the view matrix in glModelViewMatrix field
329 fw('%s<TransformInfo DEF="%s" outputGLMatrices="true" />\n' % (ident, H3D_TOP_LEVEL))
331 return ident
333 def writeFooter(ident):
335 if use_h3d:
336 # global
337 for route in h3d_material_route:
338 fw('%s%s\n' % (ident, route))
340 ident = ident[:-1]
341 fw('%s</Scene>\n' % ident)
342 ident = ident[:-1]
343 fw('%s</X3D>' % ident)
344 return 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):
363 if world:
364 mtype = world.mist_settings.falloff
365 mparam = world.mist_settings
366 else:
367 return
369 if mparam.use_mist:
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')
376 else:
377 return
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)
393 else:
394 fw('\n')
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')
405 ident += '\t'
406 return ident
408 def writeTransform_end(ident):
409 ident = ident[:-1]
410 fw('%s</Transform>\n' % ident)
411 return 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="_"))
417 if world:
418 ambi = world.ambient_color
419 amb_intensity = ((ambi[0] + ambi[1] + ambi[2]) / 3.0) / 2.5
420 del ambi
421 else:
422 amb_intensity = 0.0
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="_"))
453 if world:
454 ambi = world.ambient_color
455 # ambi = world.amb
456 amb_intensity = ((float(ambi[0] + ambi[1] + ambi[2])) / 3.0) / 2.5
457 else:
458 ambi = 0
459 amb_intensity = 0.0
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="_"))
478 if world:
479 ambi = world.ambient_color
480 # ambi = world.amb
481 amb_intensity = ((float(ambi[0] + ambi[1] + ambi[2])) / 3.0) / 2.5
482 else:
483 ambi = 0.0
484 amb_intensity = 0.0
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:
512 return
514 use_collnode = bool([mod for mod in obj.modifiers
515 if mod.type == 'COLLISION'
516 if mod.show_viewport])
518 if use_collnode:
519 fw('%s<Collision enabled="true">\n' % ident)
520 ident += '\t'
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))
526 if mesh.tag:
527 fw('%s<Group USE=%s />\n' % (ident, mesh_id_group))
528 else:
529 mesh.tag = True
531 fw('%s<Group DEF=%s>\n' % (ident, mesh_id_group))
532 ident += '\t'
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):
548 if material:
549 for mtex in material.texture_slots:
550 if mtex:
551 tex = mtex.texture
552 if tex and tex.type == 'IMAGE':
553 image = tex.image
554 if image:
555 mesh_material_tex[i] = tex
556 mesh_material_mtex[i] = mtex
557 mesh_material_images[i] = image
558 break
560 mesh_materials_use_face_texture = [getattr(material, 'use_face_texture', True) for material in mesh_materials]
562 # fast access!
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)
578 else:
579 mesh_faces_image = [None] * len(mesh_faces)
580 mesh_faces_image_unique = {None}
582 # group faces
583 face_groups = {}
584 for material_index in range(len(mesh_materials)):
585 for image in mesh_faces_image_unique:
586 face_groups[material_index, image] = []
587 del mesh_faces_image_unique
589 for i, (material_index, image) in enumerate(zip(mesh_faces_materials, mesh_faces_image)):
590 face_groups[material_index, image].append(i)
592 # same as face_groups.items() but sorted so we can get predictable output.
593 face_groups_items = list(face_groups.items())
594 face_groups_items.sort(key=lambda m: (m[0][0], getattr(m[0][1], 'name', '')))
596 for (material_index, image), face_group in face_groups_items: # face_groups.items()
597 if face_group:
598 material = mesh_materials[material_index]
600 fw('%s<Shape>\n' % ident)
601 ident += '\t'
603 is_smooth = False
604 is_col = (mesh.tessface_vertex_colors.active and (material is None or material.use_vertex_color_paint))
606 # kludge but as good as it gets!
607 for i in face_group:
608 if mesh_faces[i].use_smooth:
609 is_smooth = True
610 break
612 # UV's and VCols split verts off which effects smoothing
613 # force writing normals in this case.
614 # Also, creaseAngle is not supported for IndexedTriangleSet,
615 # so write normals when is_smooth (otherwise
616 # IndexedTriangleSet can have only all smooth/all flat shading).
617 is_force_normals = use_triangulate and (is_smooth or is_uv or is_col)
619 if use_h3d:
620 gpu_shader = gpu_shader_cache.get(material) # material can be 'None', uses dummy cache
621 if gpu_shader is None:
622 gpu_shader = gpu_shader_cache[material] = gpu.export_shader(scene, material)
624 if 1: # XXX DEBUG
625 gpu_shader_tmp = gpu.export_shader(scene, material)
626 import pprint
627 print('\nWRITING MATERIAL:', material.name)
628 del gpu_shader_tmp['fragment']
629 del gpu_shader_tmp['vertex']
630 pprint.pprint(gpu_shader_tmp, width=120)
631 #pprint.pprint(val['vertex'])
632 del gpu_shader_tmp
634 fw('%s<Appearance>\n' % ident)
635 ident += '\t'
637 if image and not use_h3d:
638 writeImageTexture(ident, image)
640 if mesh_materials_use_face_texture[material_index]:
641 if image.use_tiles:
642 fw('%s<TextureTransform scale="%s %s" />\n' % (ident, image.tiles_x, image.tiles_y))
643 else:
644 # transform by mtex
645 loc = mesh_material_mtex[material_index].offset[:2]
647 # mtex_scale * tex_repeat
648 sca_x, sca_y = mesh_material_mtex[material_index].scale[:2]
650 sca_x *= mesh_material_tex[material_index].repeat_x
651 sca_y *= mesh_material_tex[material_index].repeat_y
653 # flip x/y is a sampling feature, convert to transform
654 if mesh_material_tex[material_index].use_flip_axis:
655 rot = math.pi / -2.0
656 sca_x, sca_y = sca_y, -sca_x
657 else:
658 rot = 0.0
660 ident_step = ident + (' ' * (-len(ident) + \
661 fw('%s<TextureTransform ' % ident)))
662 fw('\n')
663 # fw('center="%.6f %.6f" ' % (0.0, 0.0))
664 fw(ident_step + 'translation="%.6f %.6f"\n' % loc)
665 fw(ident_step + 'scale="%.6f %.6f"\n' % (sca_x, sca_y))
666 fw(ident_step + 'rotation="%.6f"\n' % rot)
667 fw(ident_step + '/>\n')
669 if use_h3d:
670 mat_tmp = material if material else gpu_shader_dummy_mat
671 writeMaterialH3D(ident, mat_tmp, world,
672 obj, gpu_shader)
673 del mat_tmp
674 else:
675 if material:
676 writeMaterial(ident, material, world)
678 ident = ident[:-1]
679 fw('%s</Appearance>\n' % ident)
681 mesh_faces_col = mesh.tessface_vertex_colors.active.data if is_col else None
682 mesh_faces_uv = mesh.tessface_uv_textures.active.data if is_uv else None
684 #-- IndexedFaceSet or IndexedLineSet
685 if use_triangulate:
686 ident_step = ident + (' ' * (-len(ident) + \
687 fw('%s<IndexedTriangleSet ' % ident)))
689 # --- Write IndexedTriangleSet Attributes (same as IndexedFaceSet)
690 fw('solid="%s"\n' % bool_as_str(material and material.game_settings.use_backface_culling))
692 if use_normals or is_force_normals:
693 fw(ident_step + 'normalPerVertex="true"\n')
694 else:
695 # Tell X3D browser to generate flat (per-face) normals
696 fw(ident_step + 'normalPerVertex="false"\n')
698 slot_uv = None
699 slot_col = None
701 if is_uv and is_col:
702 slot_uv = 0
703 slot_col = 1
705 def vertex_key(fidx, f_cnr_idx):
706 return (
707 mesh_faces_uv[fidx].uv[f_cnr_idx][:],
708 getattr(mesh_faces_col[fidx], "color%d" % (f_cnr_idx + 1))[:],
710 elif is_uv:
711 slot_uv = 0
713 def vertex_key(fidx, f_cnr_idx):
714 return (
715 mesh_faces_uv[fidx].uv[f_cnr_idx][:],
717 elif is_col:
718 slot_col = 0
720 def vertex_key(fidx, f_cnr_idx):
721 return (
722 getattr(mesh_faces_col[fidx], "color%d" % (f_cnr_idx + 1))[:],
724 else:
725 # ack, not especially efficient in this case
726 def vertex_key(fidx, f_cnr_idx):
727 return None
729 # build a mesh mapping dict
730 vertex_hash = [{} for i in range(len(mesh.vertices))]
731 # worst case every face is a quad
732 face_tri_list = [[None, None, None] for i in range(len(mesh.tessfaces) * 2)]
733 vert_tri_list = []
734 totvert = 0
735 totface = 0
736 temp_face = [None] * 4
737 for i in face_group:
738 fv = mesh_faces_vertices[i]
739 for j, v_idx in enumerate(fv):
740 key = vertex_key(i, j)
741 vh = vertex_hash[v_idx]
742 x3d_v = vh.get(key)
743 if x3d_v is None:
744 x3d_v = key, v_idx, totvert
745 vh[key] = x3d_v
746 # key / original_vertex / new_vertex
747 vert_tri_list.append(x3d_v)
748 totvert += 1
749 temp_face[j] = x3d_v
751 if len(fv) == 4:
752 f_iter = ((0, 1, 2), (0, 2, 3))
753 else:
754 f_iter = ((0, 1, 2), )
756 for f_it in f_iter:
757 # loop over a quad as 2 tris
758 f_tri = face_tri_list[totface]
759 for ji, j in enumerate(f_it):
760 f_tri[ji] = temp_face[j]
761 # quads run this twice
762 totface += 1
764 # clear unused faces
765 face_tri_list[totface:] = []
767 fw(ident_step + 'index="')
768 for x3d_f in face_tri_list:
769 fw('%i %i %i ' % (x3d_f[0][2], x3d_f[1][2], x3d_f[2][2]))
770 fw('"\n')
772 # close IndexedTriangleSet
773 fw(ident_step + '>\n')
774 ident += '\t'
776 fw('%s<Coordinate ' % ident)
777 fw('point="')
778 for x3d_v in vert_tri_list:
779 fw('%.6f %.6f %.6f ' % mesh_vertices[x3d_v[1]].co[:])
780 fw('" />\n')
782 if use_normals or is_force_normals:
783 fw('%s<Normal ' % ident)
784 fw('vector="')
785 for x3d_v in vert_tri_list:
786 fw('%.6f %.6f %.6f ' % mesh_vertices[x3d_v[1]].normal[:])
787 fw('" />\n')
789 if is_uv:
790 fw('%s<TextureCoordinate point="' % ident)
791 for x3d_v in vert_tri_list:
792 fw('%.4f %.4f ' % x3d_v[0][slot_uv])
793 fw('" />\n')
795 if is_col:
796 fw('%s<Color color="' % ident)
797 for x3d_v in vert_tri_list:
798 fw('%.3f %.3f %.3f ' % x3d_v[0][slot_col])
799 fw('" />\n')
801 if use_h3d:
802 # write attributes
803 for gpu_attr in gpu_shader['attributes']:
805 # UVs
806 if gpu_attr['type'] == gpu.CD_MTFACE:
807 if gpu_attr['datatype'] == gpu.GPU_DATA_2F:
808 fw('%s<FloatVertexAttribute ' % ident)
809 fw('name="%s" ' % gpu_attr['varname'])
810 fw('numComponents="2" ')
811 fw('value="')
812 for x3d_v in vert_tri_list:
813 fw('%.4f %.4f ' % x3d_v[0][slot_uv])
814 fw('" />\n')
815 else:
816 assert(0)
818 elif gpu_attr['type'] == gpu.CD_MCOL:
819 if gpu_attr['datatype'] == gpu.GPU_DATA_4UB:
820 pass # XXX, H3D can't do
821 else:
822 assert(0)
824 ident = ident[:-1]
826 fw('%s</IndexedTriangleSet>\n' % ident)
828 else:
829 ident_step = ident + (' ' * (-len(ident) + \
830 fw('%s<IndexedFaceSet ' % ident)))
832 # --- Write IndexedFaceSet Attributes (same as IndexedTriangleSet)
833 fw('solid="%s"\n' % bool_as_str(material and material.game_settings.use_backface_culling))
834 if is_smooth:
835 # use Auto-Smooth angle, if enabled. Otherwise make
836 # the mesh perfectly smooth by creaseAngle > pi.
837 fw(ident_step + 'creaseAngle="%.4f"\n' % (mesh.auto_smooth_angle if mesh.use_auto_smooth else 4.0))
839 if use_normals:
840 # currently not optional, could be made so:
841 fw(ident_step + 'normalPerVertex="true"\n')
843 # IndexedTriangleSet assumes true
844 if is_col:
845 fw(ident_step + 'colorPerVertex="false"\n')
847 # for IndexedTriangleSet we use a uv per vertex so this isnt needed.
848 if is_uv:
849 fw(ident_step + 'texCoordIndex="')
851 j = 0
852 for i in face_group:
853 if len(mesh_faces_vertices[i]) == 4:
854 fw('%d %d %d %d -1 ' % (j, j + 1, j + 2, j + 3))
855 j += 4
856 else:
857 fw('%d %d %d -1 ' % (j, j + 1, j + 2))
858 j += 3
859 fw('"\n')
860 # --- end texCoordIndex
862 if True:
863 fw(ident_step + 'coordIndex="')
864 for i in face_group:
865 fv = mesh_faces_vertices[i]
866 if len(fv) == 3:
867 fw('%i %i %i -1 ' % fv)
868 else:
869 fw('%i %i %i %i -1 ' % fv)
871 fw('"\n')
872 # --- end coordIndex
874 # close IndexedFaceSet
875 fw(ident_step + '>\n')
876 ident += '\t'
878 # --- Write IndexedFaceSet Elements
879 if True:
880 if is_coords_written:
881 fw('%s<Coordinate USE=%s />\n' % (ident, mesh_id_coords))
882 if use_normals:
883 fw('%s<Normal USE=%s />\n' % (ident, mesh_id_normals))
884 else:
885 ident_step = ident + (' ' * (-len(ident) + \
886 fw('%s<Coordinate ' % ident)))
887 fw('DEF=%s\n' % mesh_id_coords)
888 fw(ident_step + 'point="')
889 for v in mesh.vertices:
890 fw('%.6f %.6f %.6f ' % v.co[:])
891 fw('"\n')
892 fw(ident_step + '/>\n')
894 is_coords_written = True
896 if use_normals:
897 ident_step = ident + (' ' * (-len(ident) + \
898 fw('%s<Normal ' % ident)))
899 fw('DEF=%s\n' % mesh_id_normals)
900 fw(ident_step + 'vector="')
901 for v in mesh.vertices:
902 fw('%.6f %.6f %.6f ' % v.normal[:])
903 fw('"\n')
904 fw(ident_step + '/>\n')
906 if is_uv:
907 fw('%s<TextureCoordinate point="' % ident)
908 for i in face_group:
909 for uv in mesh_faces_uv[i].uv:
910 fw('%.4f %.4f ' % uv[:])
911 del mesh_faces_uv
912 fw('" />\n')
914 if is_col:
915 fw('%s<Color color="' % ident)
916 # XXX, 1 color per face, only
917 for i in face_group:
918 fw('%.3f %.3f %.3f ' % mesh_faces_col[i].color1[:])
919 fw('" />\n')
921 #--- output vertexColors
923 #--- output closing braces
924 ident = ident[:-1]
926 fw('%s</IndexedFaceSet>\n' % ident)
928 ident = ident[:-1]
929 fw('%s</Shape>\n' % ident)
931 # XXX
933 #fw('%s<PythonScript DEF="PS" url="object.py" >\n' % ident)
934 #fw('%s <ShaderProgram USE="MA_Material.005" containerField="references"/>\n' % ident)
935 #fw('%s</PythonScript>\n' % ident)
937 ident = ident[:-1]
938 fw('%s</Group>\n' % ident)
940 ident = writeTransform_end(ident)
942 if use_collnode:
943 ident = ident[:-1]
944 fw('%s</Collision>\n' % ident)
946 def writeMaterial(ident, material, world):
947 material_id = quoteattr(unique_name(material, MA_ + material.name, uuid_cache_material, clean_func=clean_def, sep="_"))
949 # look up material name, use it if available
950 if material.tag:
951 fw('%s<Material USE=%s />\n' % (ident, material_id))
952 else:
953 material.tag = True
955 emit = material.emit
956 ambient = material.ambient / 3.0
957 diffuseColor = material.diffuse_color[:]
958 if world:
959 ambiColor = ((material.ambient * 2.0) * world.ambient_color)[:]
960 else:
961 ambiColor = 0.0, 0.0, 0.0
963 emitColor = tuple(((c * emit) + ambiColor[i]) / 2.0 for i, c in enumerate(diffuseColor))
964 shininess = material.specular_hardness / 512.0
965 specColor = tuple((c + 0.001) / (1.25 / (material.specular_intensity + 0.001)) for c in material.specular_color)
966 transp = 1.0 - material.alpha
968 if material.use_shadeless:
969 ambient = 1.0
970 shininess = 0.0
971 specColor = emitColor = diffuseColor
973 ident_step = ident + (' ' * (-len(ident) + \
974 fw('%s<Material ' % ident)))
975 fw('DEF=%s\n' % material_id)
976 fw(ident_step + 'diffuseColor="%.3f %.3f %.3f"\n' % clamp_color(diffuseColor))
977 fw(ident_step + 'specularColor="%.3f %.3f %.3f"\n' % clamp_color(specColor))
978 fw(ident_step + 'emissiveColor="%.3f %.3f %.3f"\n' % clamp_color(emitColor))
979 fw(ident_step + 'ambientIntensity="%.3f"\n' % ambient)
980 fw(ident_step + 'shininess="%.3f"\n' % shininess)
981 fw(ident_step + 'transparency="%s"\n' % transp)
982 fw(ident_step + '/>\n')
984 def writeMaterialH3D(ident, material, world,
985 obj, gpu_shader):
986 material_id = quoteattr(unique_name(material, 'MA_' + material.name, uuid_cache_material, clean_func=clean_def, sep="_"))
988 fw('%s<Material />\n' % ident)
989 if material.tag:
990 fw('%s<ComposedShader USE=%s />\n' % (ident, material_id))
991 else:
992 material.tag = True
994 # GPU_material_bind_uniforms
995 # GPU_begin_object_materials
997 #~ CD_MCOL 6
998 #~ CD_MTFACE 5
999 #~ CD_ORCO 14
1000 #~ CD_TANGENT 18
1001 #~ GPU_DATA_16F 7
1002 #~ GPU_DATA_1F 2
1003 #~ GPU_DATA_1I 1
1004 #~ GPU_DATA_2F 3
1005 #~ GPU_DATA_3F 4
1006 #~ GPU_DATA_4F 5
1007 #~ GPU_DATA_4UB 8
1008 #~ GPU_DATA_9F 6
1009 #~ GPU_DYNAMIC_LAMP_DYNCO 7
1010 #~ GPU_DYNAMIC_LAMP_DYNCOL 11
1011 #~ GPU_DYNAMIC_LAMP_DYNENERGY 10
1012 #~ GPU_DYNAMIC_LAMP_DYNIMAT 8
1013 #~ GPU_DYNAMIC_LAMP_DYNPERSMAT 9
1014 #~ GPU_DYNAMIC_LAMP_DYNVEC 6
1015 #~ GPU_DYNAMIC_OBJECT_COLOR 5
1016 #~ GPU_DYNAMIC_OBJECT_IMAT 4
1017 #~ GPU_DYNAMIC_OBJECT_MAT 2
1018 #~ GPU_DYNAMIC_OBJECT_VIEWIMAT 3
1019 #~ GPU_DYNAMIC_OBJECT_VIEWMAT 1
1020 #~ GPU_DYNAMIC_SAMPLER_2DBUFFER 12
1021 #~ GPU_DYNAMIC_SAMPLER_2DIMAGE 13
1022 #~ GPU_DYNAMIC_SAMPLER_2DSHADOW 14
1025 inline const char* typeToString( X3DType t ) {
1026 switch( t ) {
1027 case SFFLOAT: return "SFFloat";
1028 case MFFLOAT: return "MFFloat";
1029 case SFDOUBLE: return "SFDouble";
1030 case MFDOUBLE: return "MFDouble";
1031 case SFTIME: return "SFTime";
1032 case MFTIME: return "MFTime";
1033 case SFINT32: return "SFInt32";
1034 case MFINT32: return "MFInt32";
1035 case SFVEC2F: return "SFVec2f";
1036 case MFVEC2F: return "MFVec2f";
1037 case SFVEC2D: return "SFVec2d";
1038 case MFVEC2D: return "MFVec2d";
1039 case SFVEC3F: return "SFVec3f";
1040 case MFVEC3F: return "MFVec3f";
1041 case SFVEC3D: return "SFVec3d";
1042 case MFVEC3D: return "MFVec3d";
1043 case SFVEC4F: return "SFVec4f";
1044 case MFVEC4F: return "MFVec4f";
1045 case SFVEC4D: return "SFVec4d";
1046 case MFVEC4D: return "MFVec4d";
1047 case SFBOOL: return "SFBool";
1048 case MFBOOL: return "MFBool";
1049 case SFSTRING: return "SFString";
1050 case MFSTRING: return "MFString";
1051 case SFNODE: return "SFNode";
1052 case MFNODE: return "MFNode";
1053 case SFCOLOR: return "SFColor";
1054 case MFCOLOR: return "MFColor";
1055 case SFCOLORRGBA: return "SFColorRGBA";
1056 case MFCOLORRGBA: return "MFColorRGBA";
1057 case SFROTATION: return "SFRotation";
1058 case MFROTATION: return "MFRotation";
1059 case SFQUATERNION: return "SFQuaternion";
1060 case MFQUATERNION: return "MFQuaternion";
1061 case SFMATRIX3F: return "SFMatrix3f";
1062 case MFMATRIX3F: return "MFMatrix3f";
1063 case SFMATRIX4F: return "SFMatrix4f";
1064 case MFMATRIX4F: return "MFMatrix4f";
1065 case SFMATRIX3D: return "SFMatrix3d";
1066 case MFMATRIX3D: return "MFMatrix3d";
1067 case SFMATRIX4D: return "SFMatrix4d";
1068 case MFMATRIX4D: return "MFMatrix4d";
1069 case UNKNOWN_X3D_TYPE:
1070 default:return "UNKNOWN_X3D_TYPE";
1072 import gpu
1074 fw('%s<ComposedShader DEF=%s language="GLSL" >\n' % (ident, material_id))
1075 ident += '\t'
1077 shader_url_frag = 'shaders/%s_%s.frag' % (filename_strip, material_id[1:-1])
1078 shader_url_vert = 'shaders/%s_%s.vert' % (filename_strip, material_id[1:-1])
1080 # write files
1081 shader_dir = os.path.join(base_dst, 'shaders')
1082 if not os.path.isdir(shader_dir):
1083 os.mkdir(shader_dir)
1085 # ------------------------------------------------------
1086 # shader-patch
1087 field_descr = " <!--- H3D View Matrix Patch -->"
1088 fw('%s<field name="%s" type="SFMatrix4f" accessType="inputOutput" />%s\n' % (ident, H3D_VIEW_MATRIX, field_descr))
1089 frag_vars = ["uniform mat4 %s;" % H3D_VIEW_MATRIX]
1091 # annoying!, we need to track if some of the directional lamp
1092 # vars are children of the camera or not, since this adjusts how
1093 # they are patched.
1094 frag_uniform_var_map = {}
1096 h3d_material_route.append(
1097 '<ROUTE fromNode="%s" fromField="glModelViewMatrix" toNode=%s toField="%s" />%s' %
1098 (H3D_TOP_LEVEL, material_id, H3D_VIEW_MATRIX, field_descr))
1099 # ------------------------------------------------------
1101 for uniform in gpu_shader['uniforms']:
1102 if uniform['type'] == gpu.GPU_DYNAMIC_SAMPLER_2DIMAGE:
1103 field_descr = " <!--- Dynamic Sampler 2d Image -->"
1104 fw('%s<field name="%s" type="SFNode" accessType="inputOutput">%s\n' % (ident, uniform['varname'], field_descr))
1105 writeImageTexture(ident + '\t', uniform['image'])
1106 fw('%s</field>\n' % ident)
1108 elif uniform['type'] == gpu.GPU_DYNAMIC_LAMP_DYNCO:
1109 lamp_obj = uniform['lamp']
1110 frag_uniform_var_map[uniform['varname']] = lamp_obj
1112 if uniform['datatype'] == gpu.GPU_DATA_3F: # should always be true!
1113 lamp_obj_id = quoteattr(unique_name(lamp_obj, LA_ + lamp_obj.name, uuid_cache_lamp, clean_func=clean_def, sep="_"))
1114 lamp_obj_transform_id = quoteattr(unique_name(lamp_obj, lamp_obj.name, uuid_cache_object, clean_func=clean_def, sep="_"))
1116 value = '%.6f %.6f %.6f' % (global_matrix * lamp_obj.matrix_world).to_translation()[:]
1117 field_descr = " <!--- Lamp DynCo '%s' -->" % lamp_obj.name
1118 fw('%s<field name="%s" type="SFVec3f" accessType="inputOutput" value="%s" />%s\n' % (ident, uniform['varname'], value, field_descr))
1120 # ------------------------------------------------------
1121 # shader-patch
1122 field_descr = " <!--- Lamp DynCo '%s' (shader patch) -->" % lamp_obj.name
1123 fw('%s<field name="%s_transform" type="SFMatrix4f" accessType="inputOutput" />%s\n' % (ident, uniform['varname'], field_descr))
1125 # transform
1126 frag_vars.append("uniform mat4 %s_transform;" % uniform['varname'])
1127 h3d_material_route.append(
1128 '<ROUTE fromNode=%s fromField="accumulatedForward" toNode=%s toField="%s_transform" />%s' %
1129 (suffix_quoted_str(lamp_obj_transform_id, _TRANSFORM), material_id, uniform['varname'], field_descr))
1131 h3d_material_route.append(
1132 '<ROUTE fromNode=%s fromField="location" toNode=%s toField="%s" /> %s' %
1133 (lamp_obj_id, material_id, uniform['varname'], field_descr))
1134 # ------------------------------------------------------
1136 else:
1137 assert(0)
1139 elif uniform['type'] == gpu.GPU_DYNAMIC_LAMP_DYNCOL:
1140 # odd we have both 3, 4 types.
1141 lamp_obj = uniform['lamp']
1142 frag_uniform_var_map[uniform['varname']] = lamp_obj
1144 lamp = lamp_obj.data
1145 value = '%.6f %.6f %.6f' % (lamp.color * lamp.energy)[:]
1146 field_descr = " <!--- Lamp DynColor '%s' -->" % lamp_obj.name
1147 if uniform['datatype'] == gpu.GPU_DATA_3F:
1148 fw('%s<field name="%s" type="SFVec3f" accessType="inputOutput" value="%s" />%s\n' % (ident, uniform['varname'], value, field_descr))
1149 elif uniform['datatype'] == gpu.GPU_DATA_4F:
1150 fw('%s<field name="%s" type="SFVec4f" accessType="inputOutput" value="%s 1.0" />%s\n' % (ident, uniform['varname'], value, field_descr))
1151 else:
1152 assert(0)
1154 elif uniform['type'] == gpu.GPU_DYNAMIC_LAMP_DYNENERGY:
1155 # not used ?
1156 assert(0)
1158 elif uniform['type'] == gpu.GPU_DYNAMIC_LAMP_DYNVEC:
1159 lamp_obj = uniform['lamp']
1160 frag_uniform_var_map[uniform['varname']] = lamp_obj
1162 if uniform['datatype'] == gpu.GPU_DATA_3F:
1163 lamp_obj = uniform['lamp']
1164 value = '%.6f %.6f %.6f' % ((global_matrix * lamp_obj.matrix_world).to_quaternion() * mathutils.Vector((0.0, 0.0, 1.0))).normalized()[:]
1165 field_descr = " <!--- Lamp DynDirection '%s' -->" % lamp_obj.name
1166 fw('%s<field name="%s" type="SFVec3f" accessType="inputOutput" value="%s" />%s\n' % (ident, uniform['varname'], value, field_descr))
1168 # route so we can have the lamp update the view
1169 if h3d_is_object_view(scene, lamp_obj):
1170 lamp_id = quoteattr(unique_name(lamp_obj, LA_ + lamp_obj.name, uuid_cache_lamp, clean_func=clean_def, sep="_"))
1171 h3d_material_route.append(
1172 '<ROUTE fromNode=%s fromField="direction" toNode=%s toField="%s" />%s' %
1173 (lamp_id, material_id, uniform['varname'], field_descr))
1175 else:
1176 assert(0)
1178 elif uniform['type'] == gpu.GPU_DYNAMIC_OBJECT_VIEWIMAT:
1179 frag_uniform_var_map[uniform['varname']] = None
1180 if uniform['datatype'] == gpu.GPU_DATA_16F:
1181 field_descr = " <!--- Object View Matrix Inverse '%s' -->" % obj.name
1182 fw('%s<field name="%s" type="SFMatrix4f" accessType="inputOutput" />%s\n' % (ident, uniform['varname'], field_descr))
1184 h3d_material_route.append(
1185 '<ROUTE fromNode="%s" fromField="glModelViewMatrixInverse" toNode=%s toField="%s" />%s' %
1186 (H3D_TOP_LEVEL, material_id, uniform['varname'], field_descr))
1187 else:
1188 assert(0)
1190 elif uniform['type'] == gpu.GPU_DYNAMIC_OBJECT_IMAT:
1191 frag_uniform_var_map[uniform['varname']] = None
1192 if uniform['datatype'] == gpu.GPU_DATA_16F:
1193 value = ' '.join(['%.6f' % f for v in (global_matrix * obj.matrix_world).inverted().transposed() for f in v])
1194 field_descr = " <!--- Object Invertex Matrix '%s' -->" % obj.name
1195 fw('%s<field name="%s" type="SFMatrix4f" accessType="inputOutput" value="%s" />%s\n' % (ident, uniform['varname'], value, field_descr))
1196 else:
1197 assert(0)
1199 elif uniform['type'] == gpu.GPU_DYNAMIC_SAMPLER_2DSHADOW:
1200 pass # XXX, shadow buffers not supported.
1202 elif uniform['type'] == gpu.GPU_DYNAMIC_SAMPLER_2DBUFFER:
1203 frag_uniform_var_map[uniform['varname']] = None
1205 if uniform['datatype'] == gpu.GPU_DATA_1I:
1206 if 1:
1207 tex = uniform['texpixels']
1208 value = []
1209 for i in range(0, len(tex) - 1, 4):
1210 col = tex[i:i + 4]
1211 value.append('0x%.2x%.2x%.2x%.2x' % (col[0], col[1], col[2], col[3]))
1213 field_descr = " <!--- Material Buffer -->"
1214 fw('%s<field name="%s" type="SFNode" accessType="inputOutput">%s\n' % (ident, uniform['varname'], field_descr))
1216 ident += '\t'
1218 ident_step = ident + (' ' * (-len(ident) + \
1219 fw('%s<PixelTexture \n' % ident)))
1220 fw(ident_step + 'repeatS="false"\n')
1221 fw(ident_step + 'repeatT="false"\n')
1223 fw(ident_step + 'image="%s 1 4 %s"\n' % (len(value), " ".join(value)))
1225 fw(ident_step + '/>\n')
1227 ident = ident[:-1]
1229 fw('%s</field>\n' % ident)
1231 #for i in range(0, 10, 4)
1232 #value = ' '.join(['%d' % f for f in uniform['texpixels']])
1233 # value = ' '.join(['%.6f' % (f / 256) for f in uniform['texpixels']])
1235 #fw('%s<field name="%s" type="SFInt32" accessType="inputOutput" value="%s" />%s\n' % (ident, uniform['varname'], value, field_descr))
1236 #print('test', len(uniform['texpixels']))
1237 else:
1238 assert(0)
1239 else:
1240 print("SKIPPING", uniform['type'])
1242 file_frag = open(os.path.join(base_dst, shader_url_frag), 'w', encoding='utf-8')
1243 file_frag.write(gpu_shader['fragment'])
1244 file_frag.close()
1245 # patch it
1246 h3d_shader_glsl_frag_patch(os.path.join(base_dst, shader_url_frag),
1247 scene,
1248 frag_vars,
1249 frag_uniform_var_map,
1252 file_vert = open(os.path.join(base_dst, shader_url_vert), 'w', encoding='utf-8')
1253 file_vert.write(gpu_shader['vertex'])
1254 file_vert.close()
1256 fw('%s<ShaderPart type="FRAGMENT" url=%s />\n' % (ident, quoteattr(shader_url_frag)))
1257 fw('%s<ShaderPart type="VERTEX" url=%s />\n' % (ident, quoteattr(shader_url_vert)))
1258 ident = ident[:-1]
1260 fw('%s</ComposedShader>\n' % ident)
1262 def writeImageTexture(ident, image):
1263 image_id = quoteattr(unique_name(image, IM_ + image.name, uuid_cache_image, clean_func=clean_def, sep="_"))
1265 if image.tag:
1266 fw('%s<ImageTexture USE=%s />\n' % (ident, image_id))
1267 else:
1268 image.tag = True
1270 ident_step = ident + (' ' * (-len(ident) + \
1271 fw('%s<ImageTexture ' % ident)))
1272 fw('DEF=%s\n' % image_id)
1274 # collect image paths, can load multiple
1275 # [relative, name-only, absolute]
1276 filepath = image.filepath
1277 filepath_full = bpy.path.abspath(filepath, library=image.library)
1278 filepath_ref = bpy_extras.io_utils.path_reference(filepath_full, base_src, base_dst, path_mode, "textures", copy_set, image.library)
1279 filepath_base = os.path.basename(filepath_full)
1281 images = [
1282 filepath_ref,
1283 filepath_base,
1285 if path_mode != 'RELATIVE':
1286 images.append(filepath_full)
1288 images = [f.replace('\\', '/') for f in images]
1289 images = [f for i, f in enumerate(images) if f not in images[:i]]
1291 fw(ident_step + "url='%s'\n" % ' '.join(['"%s"' % escape(f) for f in images]))
1292 fw(ident_step + '/>\n')
1294 def writeBackground(ident, world):
1296 if world is None:
1297 return
1299 # note, not re-used
1300 world_id = quoteattr(unique_name(world, WO_ + world.name, uuid_cache_world, clean_func=clean_def, sep="_"))
1302 blending = world.use_sky_blend, world.use_sky_paper, world.use_sky_real
1304 grd_triple = clamp_color(world.horizon_color)
1305 sky_triple = clamp_color(world.zenith_color)
1306 mix_triple = clamp_color((grd_triple[i] + sky_triple[i]) / 2.0 for i in range(3))
1308 ident_step = ident + (' ' * (-len(ident) + \
1309 fw('%s<Background ' % ident)))
1310 fw('DEF=%s\n' % world_id)
1311 # No Skytype - just Hor color
1312 if blending == (False, False, False):
1313 fw(ident_step + 'groundColor="%.3f %.3f %.3f"\n' % grd_triple)
1314 fw(ident_step + 'skyColor="%.3f %.3f %.3f"\n' % grd_triple)
1315 # Blend Gradient
1316 elif blending == (True, False, False):
1317 fw(ident_step + 'groundColor="%.3f %.3f %.3f, %.3f %.3f %.3f"\n' % (grd_triple + mix_triple))
1318 fw(ident_step + 'groundAngle="1.57"\n')
1319 fw(ident_step + 'skyColor="%.3f %.3f %.3f, %.3f %.3f %.3f"\n' % (sky_triple + mix_triple))
1320 fw(ident_step + 'skyAngle="1.57"\n')
1321 # Blend+Real Gradient Inverse
1322 elif blending == (True, False, True):
1323 fw(ident_step + 'groundColor="%.3f %.3f %.3f, %.3f %.3f %.3f"\n' % (sky_triple + grd_triple))
1324 fw(ident_step + 'groundAngle="1.57"\n')
1325 fw(ident_step + 'skyColor="%.3f %.3f %.3f, %.3f %.3f %.3f, %.3f %.3f %.3f"\n' % (sky_triple + grd_triple + sky_triple))
1326 fw(ident_step + 'skyAngle="1.57, 3.14159"\n')
1327 # Paper - just Zen Color
1328 elif blending == (False, False, True):
1329 fw(ident_step + 'groundColor="%.3f %.3f %.3f"\n' % sky_triple)
1330 fw(ident_step + 'skyColor="%.3f %.3f %.3f"\n' % sky_triple)
1331 # Blend+Real+Paper - komplex gradient
1332 elif blending == (True, True, True):
1333 fw(ident_step + 'groundColor="%.3f %.3f %.3f, %.3f %.3f %.3f"\n' % (sky_triple + grd_triple))
1334 fw(ident_step + 'groundAngle="1.57"\n')
1335 fw(ident_step + 'skyColor="%.3f %.3f %.3f, %.3f %.3f %.3f"\n' % (sky_triple + grd_triple))
1336 fw(ident_step + 'skyAngle="1.57"\n')
1337 # Any Other two colors
1338 else:
1339 fw(ident_step + 'groundColor="%.3f %.3f %.3f"\n' % grd_triple)
1340 fw(ident_step + 'skyColor="%.3f %.3f %.3f"\n' % sky_triple)
1342 for tex in bpy.data.textures:
1343 if tex.type == 'IMAGE' and tex.image:
1344 namemat = tex.name
1345 pic = tex.image
1346 basename = quoteattr(bpy.path.basename(pic.filepath))
1348 if namemat == 'back':
1349 fw(ident_step + 'backUrl=%s\n' % basename)
1350 elif namemat == 'bottom':
1351 fw(ident_step + 'bottomUrl=%s\n' % basename)
1352 elif namemat == 'front':
1353 fw(ident_step + 'frontUrl=%s\n' % basename)
1354 elif namemat == 'left':
1355 fw(ident_step + 'leftUrl=%s\n' % basename)
1356 elif namemat == 'right':
1357 fw(ident_step + 'rightUrl=%s\n' % basename)
1358 elif namemat == 'top':
1359 fw(ident_step + 'topUrl=%s\n' % basename)
1361 fw(ident_step + '/>\n')
1363 # -------------------------------------------------------------------------
1364 # Export Object Hierarchy (recursively called)
1365 # -------------------------------------------------------------------------
1366 def export_object(ident, obj_main_parent, obj_main, obj_children):
1367 matrix_fallback = mathutils.Matrix()
1368 world = scene.world
1369 free, derived = create_derived_objects(scene, obj_main)
1371 if use_hierarchy:
1372 obj_main_matrix_world = obj_main.matrix_world
1373 if obj_main_parent:
1374 obj_main_matrix = obj_main_parent.matrix_world.inverted(matrix_fallback) * obj_main_matrix_world
1375 else:
1376 obj_main_matrix = obj_main_matrix_world
1377 obj_main_matrix_world_invert = obj_main_matrix_world.inverted(matrix_fallback)
1379 obj_main_id = quoteattr(unique_name(obj_main, obj_main.name, uuid_cache_object, clean_func=clean_def, sep="_"))
1381 ident = writeTransform_begin(ident, obj_main_matrix if obj_main_parent else global_matrix * obj_main_matrix, suffix_quoted_str(obj_main_id, _TRANSFORM))
1383 # Set here just incase we dont enter the loop below.
1384 is_dummy_tx = False
1386 for obj, obj_matrix in (() if derived is None else derived):
1387 obj_type = obj.type
1389 if use_hierarchy:
1390 # make transform node relative
1391 obj_matrix = obj_main_matrix_world_invert * obj_matrix
1392 else:
1393 obj_matrix = global_matrix * obj_matrix
1395 # H3D - use for writing a dummy transform parent
1396 is_dummy_tx = False
1398 if obj_type == 'CAMERA':
1399 writeViewpoint(ident, obj, obj_matrix, scene)
1401 if use_h3d and scene.camera == obj:
1402 view_id = uuid_cache_view[obj]
1403 fw('%s<Transform DEF="%s">\n' % (ident, H3D_CAMERA_FOLLOW))
1404 h3d_material_route.extend([
1405 '<ROUTE fromNode="%s" fromField="totalPosition" toNode="%s" toField="translation" />' % (view_id, H3D_CAMERA_FOLLOW),
1406 '<ROUTE fromNode="%s" fromField="totalOrientation" toNode="%s" toField="rotation" />' % (view_id, H3D_CAMERA_FOLLOW),
1408 is_dummy_tx = True
1409 ident += '\t'
1411 elif obj_type in {'MESH', 'CURVE', 'SURFACE', 'FONT'}:
1412 if (obj_type != 'MESH') or (use_mesh_modifiers and obj.is_modified(scene, 'PREVIEW')):
1413 try:
1414 me = obj.to_mesh(scene, use_mesh_modifiers, 'PREVIEW')
1415 except:
1416 me = None
1417 do_remove = True
1418 else:
1419 me = obj.data
1420 do_remove = False
1422 if me is not None:
1423 # ensure unique name, we could also do this by
1424 # postponing mesh removal, but clearing data - TODO
1425 if do_remove:
1426 me.name = obj.name.rstrip("1234567890").rstrip(".")
1427 me_name_new = me_name_org = me.name
1428 count = 0
1429 while me_name_new in mesh_name_set:
1430 me.name = "%.17s.%03d" % (me_name_org, count)
1431 me_name_new = me.name
1432 count += 1
1433 mesh_name_set.add(me_name_new)
1434 del me_name_new, me_name_org, count
1435 # done
1437 writeIndexedFaceSet(ident, obj, me, obj_matrix, world)
1439 # free mesh created with create_mesh()
1440 if do_remove:
1441 bpy.data.meshes.remove(me)
1443 elif obj_type == 'LAMP':
1444 data = obj.data
1445 datatype = data.type
1446 if datatype == 'POINT':
1447 writePointLight(ident, obj, obj_matrix, data, world)
1448 elif datatype == 'SPOT':
1449 writeSpotLight(ident, obj, obj_matrix, data, world)
1450 elif datatype == 'SUN':
1451 writeDirectionalLight(ident, obj, obj_matrix, data, world)
1452 else:
1453 writeDirectionalLight(ident, obj, obj_matrix, data, world)
1454 else:
1455 #print "Info: Ignoring [%s], object type [%s] not handle yet" % (object.name,object.getType)
1456 pass
1458 if free:
1459 free_derived_objects(obj_main)
1461 # ---------------------------------------------------------------------
1462 # write out children recursively
1463 # ---------------------------------------------------------------------
1464 for obj_child, obj_child_children in obj_children:
1465 export_object(ident, obj_main, obj_child, obj_child_children)
1467 if is_dummy_tx:
1468 ident = ident[:-1]
1469 fw('%s</Transform>\n' % ident)
1470 is_dummy_tx = False
1472 if use_hierarchy:
1473 ident = writeTransform_end(ident)
1475 # -------------------------------------------------------------------------
1476 # Main Export Function
1477 # -------------------------------------------------------------------------
1478 def export_main():
1479 world = scene.world
1481 # tag un-exported IDs
1482 bpy.data.meshes.tag(False)
1483 bpy.data.materials.tag(False)
1484 bpy.data.images.tag(False)
1486 if use_selection:
1487 objects = [obj for obj in scene.objects if obj.is_visible(scene) and obj.select]
1488 else:
1489 objects = [obj for obj in scene.objects if obj.is_visible(scene)]
1491 print('Info: starting X3D export to %r...' % file.name)
1492 ident = ''
1493 ident = writeHeader(ident)
1495 writeNavigationInfo(ident, scene, any(obj.type == 'LAMP' for obj in objects))
1496 writeBackground(ident, world)
1497 writeFog(ident, world)
1499 ident = '\t\t'
1501 if use_hierarchy:
1502 objects_hierarchy = build_hierarchy(objects)
1503 else:
1504 objects_hierarchy = ((obj, []) for obj in objects)
1506 for obj_main, obj_main_children in objects_hierarchy:
1507 export_object(ident, None, obj_main, obj_main_children)
1509 ident = writeFooter(ident)
1511 export_main()
1513 # -------------------------------------------------------------------------
1514 # global cleanup
1515 # -------------------------------------------------------------------------
1516 file.close()
1518 if use_h3d:
1519 bpy.data.materials.remove(gpu_shader_dummy_mat)
1521 # copy all collected files.
1522 # print(copy_set)
1523 bpy_extras.io_utils.path_reference_copy(copy_set)
1525 print('Info: finished X3D export to %r' % file.name)
1528 ##########################################################
1529 # Callbacks, needed before Main
1530 ##########################################################
1533 def gzip_open_utf8(filepath, mode):
1534 """Workaround for py3k only allowing binary gzip writing"""
1536 import gzip
1538 # need to investigate encoding
1539 file = gzip.open(filepath, mode)
1540 write_real = file.write
1542 def write_wrap(data):
1543 return write_real(data.encode("utf-8"))
1545 file.write = write_wrap
1547 return file
1550 def save(context,
1551 filepath,
1553 use_selection=True,
1554 use_mesh_modifiers=False,
1555 use_triangulate=False,
1556 use_normals=False,
1557 use_compress=False,
1558 use_hierarchy=True,
1559 use_h3d=False,
1560 global_matrix=None,
1561 path_mode='AUTO',
1562 name_decorations=True
1565 bpy.path.ensure_ext(filepath, '.x3dz' if use_compress else '.x3d')
1567 if bpy.ops.object.mode_set.poll():
1568 bpy.ops.object.mode_set(mode='OBJECT')
1570 if use_compress:
1571 file = gzip_open_utf8(filepath, 'w')
1572 else:
1573 file = open(filepath, 'w', encoding='utf-8')
1575 if global_matrix is None:
1576 global_matrix = mathutils.Matrix()
1578 export(file,
1579 global_matrix,
1580 context.scene,
1581 use_mesh_modifiers=use_mesh_modifiers,
1582 use_selection=use_selection,
1583 use_triangulate=use_triangulate,
1584 use_normals=use_normals,
1585 use_hierarchy=use_hierarchy,
1586 use_h3d=use_h3d,
1587 path_mode=path_mode,
1588 name_decorations=name_decorations,
1591 return {'FINISHED'}