From ed92022b4b1a5e4ae2702757dda21abdba966b2a Mon Sep 17 00:00:00 2001 From: alteredq Date: Sun, 3 Jul 2011 04:34:21 +0200 Subject: [PATCH] Updated Blender 2.58 exporter with recent changes from 2.57 exporter. (normal maps, textures packed in blend file, copy textures, urlBaseType, texture params) Also fixed broken reload handling and bumped up version number. --- .../addons/io_mesh_threejs/__init__.py | 30 +- .../addons/io_mesh_threejs/export_threejs.py | 321 +++++++++++++----- 2 files changed, 258 insertions(+), 93 deletions(-) diff --git a/utils/exporters/blender/2.58/scripts/addons/io_mesh_threejs/__init__.py b/utils/exporters/blender/2.58/scripts/addons/io_mesh_threejs/__init__.py index 0b2cd875e1..c92aaa9177 100755 --- a/utils/exporters/blender/2.58/scripts/addons/io_mesh_threejs/__init__.py +++ b/utils/exporters/blender/2.58/scripts/addons/io_mesh_threejs/__init__.py @@ -20,13 +20,11 @@ # Init # ################################################################ -# To support reload properly, try to access a package var, -# if it's there, reload everything bl_info = { "name": "three.js format", - "author": "mrdoob, kikko, alteredq, remoe", - "version": (1, 0), + "author": "mrdoob, kikko, alteredq, remoe, pxf", + "version": (1, 0, 2), "blender": (2, 5, 7), "api": 35622, "location": "File > Import-Export", @@ -36,6 +34,11 @@ bl_info = { "tracker_url": "https://github.com/mrdoob/three.js/issues", "category": "Import-Export"} +# To support reload properly, try to access a package var, +# if it's there, reload everything + +import bpy + if "bpy" in locals(): import imp if "export_threejs" in locals(): @@ -43,7 +46,6 @@ if "bpy" in locals(): if "import_threejs" in locals(): imp.reload(import_threejs) -import bpy from bpy.props import * from bpy_extras.io_utils import ExportHelper, ImportHelper @@ -175,7 +177,9 @@ def save_settings_export(properties): settings = { "option_export_scene" : properties.option_export_scene, "option_embed_meshes" : properties.option_embed_meshes, - + "option_url_base_html" : properties.option_url_base_html, + "option_copy_textures" : properties.option_copy_textures, + "option_lights" : properties.option_lights, "option_cameras" : properties.option_cameras, @@ -225,6 +229,8 @@ def restore_settings_export(properties): properties.option_export_scene = settings.get("option_export_scene", False) properties.option_embed_meshes = settings.get("option_embed_meshes", True) + properties.option_url_base_html = settings.get("option_url_base_html", False) + properties.option_copy_textures = settings.get("option_copy_textures", False) properties.option_lights = settings.get("option_lights", False) properties.option_cameras = settings.get("option_cameras", False) @@ -263,7 +269,9 @@ class ExportTHREEJS(bpy.types.Operator, ExportHelper): option_export_scene = BoolProperty(name = "Scene", description = "Export scene", default = False) option_embed_meshes = BoolProperty(name = "Embed", description = "Embed meshes", default = True) - + option_copy_textures = BoolProperty(name = "Copy textures", description = "Copy textures", default = False) + option_url_base_html = BoolProperty(name = "HTML as url base", description = "Use HTML as url base ", default = False) + option_lights = BoolProperty(name = "Lights", description = "Export default scene lights", default = False) option_cameras = BoolProperty(name = "Cameras", description = "Export default scene cameras", default = False) @@ -284,6 +292,7 @@ class ExportTHREEJS(bpy.types.Operator, ExportHelper): save_settings_export(self.properties) filepath = self.filepath + import io_mesh_threejs.export_threejs return io_mesh_threejs.export_threejs.save(self, context, **self.properties) @@ -341,11 +350,18 @@ class ExportTHREEJS(bpy.types.Operator, ExportHelper): row = layout.row() row.prop(self.properties, "option_export_scene") + + row = layout.row() row.prop(self.properties, "option_lights") row.prop(self.properties, "option_cameras") row = layout.row() row.prop(self.properties, "option_embed_meshes") + row.prop(self.properties, "option_copy_textures") + + row = layout.row() + row.prop(self.properties, "option_url_base_html") + layout.separator() diff --git a/utils/exporters/blender/2.58/scripts/addons/io_mesh_threejs/export_threejs.py b/utils/exporters/blender/2.58/scripts/addons/io_mesh_threejs/export_threejs.py index 24e5b66fa0..153ade43da 100644 --- a/utils/exporters/blender/2.58/scripts/addons/io_mesh_threejs/export_threejs.py +++ b/utils/exporters/blender/2.58/scripts/addons/io_mesh_threejs/export_threejs.py @@ -20,13 +20,13 @@ Blender exporter for Three.js (ASCII JSON format). TODO - - copy used images to folder where exported file goes - binary format """ import bpy import mathutils +import shutil import os import os.path import math @@ -79,7 +79,7 @@ COLORS = [0xeeeeee, 0xee0000, 0x00ee00, 0x0000ee, 0xeeee00, 0x00eeee, 0xee00ee] TEMPLATE_SCENE_ASCII = """\ /* Converted from: %(fname)s * - * File generated with Blender 2.57 Exporter + * File generated with Blender 2.58 Exporter * https://github.com/mrdoob/three.js/tree/master/utils/exporters/blender/ * * objects: %(nobjects)s @@ -91,7 +91,7 @@ TEMPLATE_SCENE_ASCII = """\ var scene = { "type" : "scene", -"urlBaseType" : "relativeToScene", +"urlBaseType" : %(basetype)s, %(sections)s @@ -222,7 +222,7 @@ TEMPLATE_HEX = "0x%06x" TEMPLATE_FILE_ASCII = """\ /* - * File generated with Blender 2.57 Exporter + * File generated with Blender 2.58 Exporter * https://github.com/mrdoob/three.js/tree/master/utils/exporters/blender/ * * vertices: %(nvertex)d @@ -693,6 +693,8 @@ def value2string(v): return '"%s"' % v elif type(v) == bool: return str(v).lower() + elif type(v) == list: + return "[%s]" % (", ".join(value2string(x) for x in v)) return str(v) def generate_materials(mtl, materials, draw_type): @@ -721,7 +723,7 @@ def generate_materials(mtl, materials, draw_type): return ",\n\n".join([m for i,m in sorted(mtl_array)]), len(mtl_array) -def extract_materials(mesh, scene, option_colors): +def extract_materials(mesh, scene, option_colors, option_copy_textures, filepath): world = scene.world materials = {} @@ -749,22 +751,16 @@ def extract_materials(mesh, scene, option_colors): material['transparency'] = m.alpha # not sure about mapping values to Blinn-Phong shader - # Blender uses INT from [1,511] with default 0 + # Blender uses INT from [1, 511] with default 0 # http://www.blender.org/documentation/blender_python_api_2_54_0/bpy.types.Material.html#bpy.types.Material.specular_hardness material["specularCoef"] = m.specular_hardness - texture = m.active_texture + textures = guess_material_textures(m) - if texture and texture.type == 'IMAGE' and texture.image: - fn = bpy.path.abspath(texture.image.filepath) - fn = os.path.normpath(fn) - fn_strip = os.path.basename(fn) - - material['mapDiffuse'] = fn_strip - - if texture.repeat_x != 1 or texture.repeat_y != 1: - material['mapDiffuseRepeat'] = [texture.repeat_x, texture.repeat_y] + handle_texture('diffuse', textures, material, filepath, option_copy_textures) + handle_texture('light', textures, material, filepath, option_copy_textures) + handle_texture('normal', textures, material, filepath, option_copy_textures) material["vertexColors"] = m.THREE_useVertexColors and option_colors @@ -779,7 +775,7 @@ def extract_materials(mesh, scene, option_colors): return materials -def generate_materials_string(mesh, scene, option_colors, draw_type): +def generate_materials_string(mesh, scene, option_colors, draw_type, option_copy_textures, filepath): random.seed(42) # to get well defined color order for debug materials @@ -801,10 +797,45 @@ def generate_materials_string(mesh, scene, option_colors, draw_type): # extract real materials from the mesh - mtl.update(extract_materials(mesh, scene, option_colors)) + mtl.update(extract_materials(mesh, scene, option_colors, option_copy_textures, filepath)) return generate_materials(mtl, materials, draw_type) +def handle_texture(id, textures, material, filepath, option_copy_textures): + + if textures[id]: + texName = 'map%s' % id.capitalize() + repeatName = 'map%sRepeat' % id.capitalize() + wrapName = 'map%sWrap' % id.capitalize() + + slot = textures[id]['slot'] + texture = textures[id]['texture'] + image = texture.image + fname = extract_texture_filename(image) + material[texName] = fname + + if option_copy_textures: + save_image(image, fname, filepath) + + if texture.repeat_x != 1 or texture.repeat_y != 1: + material[repeatName] = [texture.repeat_x, texture.repeat_y] + + if texture.extension == "REPEAT": + wrap_x = "repeat" + wrap_y = "repeat" + + if texture.use_mirror_x: + wrap_x = "mirror" + if texture.use_mirror_y: + wrap_y = "mirror" + + material[wrapName] = [wrap_x, wrap_y] + + if slot.use_map_normal: + if slot.normal_factor != 1.0: + material['mapNormalFactor'] = slot.normal_factor + + # ##################################################### # ASCII model generator # ##################################################### @@ -821,7 +852,9 @@ def generate_ascii_model(mesh, scene, align_model, flipyz, option_scale, - draw_type): + draw_type, + option_copy_textures, + filepath): vertices = mesh.vertices[:] @@ -843,7 +876,7 @@ def generate_ascii_model(mesh, scene, nedges = 0 if option_materials: - materials_string, nmaterial = generate_materials_string(mesh, scene, option_colors, draw_type) + materials_string, nmaterial = generate_materials_string(mesh, scene, option_colors, draw_type, option_copy_textures, filepath) if option_edges: nedges = len(mesh.edges) @@ -873,7 +906,7 @@ def generate_ascii_model(mesh, scene, "ncolor" : ncolor, "nmaterial" : nmaterial, "nedges" : nedges, - + "model" : model_string } @@ -897,7 +930,9 @@ def generate_mesh_string(obj, scene, align_model, flipyz, option_scale, - export_single_model): + export_single_model, + option_copy_textures, + filepath): # collapse modifiers into mesh @@ -937,6 +972,10 @@ def generate_mesh_string(obj, scene, if not active_col_layer: option_colors = False + option_copy_textures_model = False + if export_single_model and option_copy_textures: + option_copy_textures_model = True + text, model_string = generate_ascii_model(mesh, scene, option_vertices, option_vertices_truncate, @@ -949,11 +988,13 @@ def generate_mesh_string(obj, scene, align_model, flipyz, option_scale, - obj.draw_type) + obj.draw_type, + option_copy_textures_model, + filepath) # remove temp mesh bpy.data.meshes.remove(mesh) - + return text, model_string def export_mesh(obj, scene, filepath, @@ -968,7 +1009,8 @@ def export_mesh(obj, scene, filepath, align_model, flipyz, option_scale, - export_single_model): + export_single_model, + option_copy_textures): """Export single mesh""" @@ -984,7 +1026,9 @@ def export_mesh(obj, scene, filepath, align_model, flipyz, option_scale, - export_single_model) + export_single_model, + option_copy_textures, + filepath) write_file(filepath, text) @@ -1085,7 +1129,7 @@ def generate_objects(data): visible = False geometry_string = generate_string(geometry_id) - + object_string = TEMPLATE_OBJECT % { "object_id" : generate_string(object_id), "geometry_id" : geometry_string, @@ -1103,7 +1147,7 @@ def generate_objects(data): "visible" : generate_bool_property(visible) } chunks.append(object_string) - + elif obj.type == "EMPTY" or (obj.type == "MESH" and not obj.THREE_exportGeometry): object_id = obj.name @@ -1117,7 +1161,7 @@ def generate_objects(data): group_string = generate_string_list(group_ids) triggerType = obj.THREE_triggerType - + object_string = TEMPLATE_EMPTY % { "object_id" : generate_string(object_id), "group_id" : group_string, @@ -1153,7 +1197,7 @@ def generate_geometries(data): if name not in geo_set: geometry_id = "geo_%s" % name - + if data["embed_meshes"]: embed_id = "emb_%s" % name @@ -1195,10 +1239,25 @@ def generate_textures_scene(data): texture_id = img.name texture_file = extract_texture_filename(img) - + + if data["copy_textures"]: + save_image(img, texture_file, data["filepath"]) + extras = "" + if texture.repeat_x != 1 or texture.repeat_y != 1: - extras = ',\n "repeat": [%f, %f]' % (texture.repeat_x, texture.repeat_y) + extras += ',\n "repeat": [%f, %f]' % (texture.repeat_x, texture.repeat_y) + + if texture.extension == "REPEAT": + wrap_x = "repeat" + wrap_y = "repeat" + + if texture.use_mirror_x: + wrap_x = "mirror" + if texture.use_mirror_y: + wrap_y = "mirror" + + extras += ',\n "wrap": ["%s", "%s"]' % (wrap_x, wrap_y) texture_string = TEMPLATE_TEXTURE % { "texture_id" : generate_string(texture_id), @@ -1215,6 +1274,19 @@ def extract_texture_filename(image): fn_strip = os.path.basename(fn) return fn_strip +def save_image(img, name, fpath): + dst_dir = os.path.dirname(fpath) + dst_path = os.path.join(dst_dir, name) + + ensure_folder_exist(dst_dir) + + if img.packed_file: + img.save_render(dst_path) + + else: + src_path = bpy.path.abspath(img.filepath) + shutil.copy(src_path, dst_dir) + # ##################################################### # Scene exporter - materials # ##################################################### @@ -1248,38 +1320,60 @@ def extract_material_data(m, option_colors): material["specularCoef"] = m.specular_hardness + material["vertexColors"] = m.THREE_useVertexColors and option_colors + material['mapDiffuse'] = "" material['mapLight'] = "" material['mapNormal'] = "" + material['mapNormalFactor'] = 1.0 + + textures = guess_material_textures(m) + + if textures['diffuse']: + material['mapDiffuse'] = textures['diffuse']['texture'].image.name + + if textures['light']: + material['mapLight'] = textures['light']['texture'].image.name + + if textures['normal']: + material['mapNormal'] = textures['normal']['texture'].image.name + if textures['normal']['slot'].use_map_normal: + material['mapNormalFactor'] = textures['normal']['slot'].normal_factor + + material['shading'] = m.THREE_materialType + + return material + +def guess_material_textures(material): + textures = { + 'diffuse' : None, + 'light' : None, + 'normal' : None + } - material["vertexColors"] = m.THREE_useVertexColors and option_colors - # just take first textures of each, for the moment three.js materials can't handle more + # assume diffuse comes before lightmap, normalmap has checked flag - for i in range(len(m.texture_slots)): - ts = m.texture_slots[i] - if ts: - t = ts.texture - if ts.use and t.type == 'IMAGE': - name = t.image.name + for i in range(len(material.texture_slots)): + slot = material.texture_slots[i] + if slot: + texture = slot.texture + if slot.use and texture.type == 'IMAGE': - if t.use_normal_map: - material['mapNormal'] = name + if texture.use_normal_map: + textures['normal'] = { "texture": texture, "slot": slot } else: - - if not material['mapDiffuse']: - material['mapDiffuse'] = name + if not textures['diffuse']: + textures['diffuse'] = { "texture": texture, "slot": slot } else: - material['mapLight'] = name + textures['light'] = { "texture": texture, "slot": slot } - if material['mapDiffuse'] and material['mapNormal'] and material['mapLight']: + if textures['diffuse'] and textures['normal'] and textures['light']: break - material['shading'] = m.THREE_materialType - - return material + return textures def generate_material_string(material): type_map = { @@ -1302,6 +1396,7 @@ def generate_material_string(material): colorMap = material['mapDiffuse'] lightMap = material['mapLight'] normalMap = material['mapNormal'] + normalMapFactor = material['mapNormalFactor'] if colorMap: parameters += ', "map": %s' % generate_string(colorMap) @@ -1309,10 +1404,12 @@ def generate_material_string(material): parameters += ', "lightMap": %s' % generate_string(lightMap) if normalMap: parameters += ', "normalMap": %s' % generate_string(normalMap) + if normalMapFactor != 1.0: + parameters += ', "normalMapFactor": %f' % normalMapFactor if material['vertexColors']: parameters += ', "vertexColors": "vertex"' - + material_string = TEMPLATE_MATERIAL_SCENE % { "material_id" : generate_string(material_id), "type" : generate_string(material_type), @@ -1340,14 +1437,13 @@ def generate_materials_scene(data): def generate_cameras(data): if data["use_cameras"]: - cameras = data.get("cameras", []) - - if not cameras: - cameras.append(DEFAULTS["camera"]) + cams = bpy.data.objects + cams = [ob for ob in cams if (ob.type == 'CAMERA' and ob.select)] chunks = [] - for camera in cameras: + if not cams: + camera = DEFAULTS["camera"] if camera["type"] == "perspective": @@ -1377,6 +1473,29 @@ def generate_cameras(data): chunks.append(camera_string) + else: + + for cameraobj in cams: + camera = bpy.data.cameras[cameraobj.name] + + # TODO: + # Support more than perspective camera + # Calculate a target/lookat + # Get correct aspect ratio + if camera.id_data.type == "PERSP": + + camera_string = TEMPLATE_CAMERA_PERSPECTIVE % { + "camera_id" : generate_string(camera.name), + "fov" : (camera.angle / 3.14) * 180.0, + "aspect" : 1.333, + "near" : camera.clip_start, + "far" : camera.clip_end, + "position" : generate_vec3([cameraobj.location[0], -cameraobj.location[1], cameraobj.location[2]]), + "target" : generate_vec3([0, 0, 0]) + } + + chunks.append(camera_string) + return ",\n\n".join(chunks) return "" @@ -1417,7 +1536,7 @@ def generate_lights(data): chunks.append(light_string) return ",\n\n".join(chunks) - + return "" # ##################################################### @@ -1425,20 +1544,20 @@ def generate_lights(data): # ##################################################### def generate_embeds(data): - + if data["embed_meshes"]: chunks = [] - + for e in data["embeds"]: - + embed = '"emb_%s": {%s}' % (e, data["embeds"][e]) chunks.append(embed) - + return ",\n\n".join(chunks) return "" - + # ##################################################### # Scene exporter - generate ASCII scene # ##################################################### @@ -1455,6 +1574,13 @@ def generate_ascii_scene(data): embeds = generate_embeds(data) + basetype = "relativeTo" + + if data["base_html"]: + basetype += "HTML" + else: + basetype += "Scene" + sections = [ ["objects", objects], ["geometries", geometries], @@ -1474,7 +1600,11 @@ def generate_ascii_scene(data): default_camera = "" if data["use_cameras"]: - default_camera = "default_camera" + cams = [ob for ob in bpy.data.objects if (ob.type == 'CAMERA' and ob.select)] + if not cams: + default_camera = "default_camera" + else: + default_camera = cams[0].name parameters = { "fname" : data["source_file"], @@ -1488,6 +1618,7 @@ def generate_ascii_scene(data): "nobjects" : nobjects, "ngeometries" : ngeometries, "ntextures" : ntextures, + "basetype" : generate_string(basetype), "nmaterials" : nmaterials, "position" : generate_vec3(DEFAULTS["position"]), @@ -1499,22 +1630,24 @@ def generate_ascii_scene(data): return text -def export_scene(scene, filepath, flipyz, option_colors, option_lights, option_cameras, option_embed_meshes, embeds): +def export_scene(scene, filepath, flipyz, option_colors, option_lights, option_cameras, option_embed_meshes, embeds, option_url_base_html, option_copy_textures): source_file = os.path.basename(bpy.data.filepath) scene_text = "" data = { - "scene" : scene, - "objects" : scene.objects, - "embeds" : embeds, - "source_file" : source_file, - "filepath" : filepath, - "flipyz" : flipyz, - "use_colors" : option_colors, - "use_lights" : option_lights, - "use_cameras" : option_cameras, - "embed_meshes": option_embed_meshes + "scene" : scene, + "objects" : scene.objects, + "embeds" : embeds, + "source_file" : source_file, + "filepath" : filepath, + "flipyz" : flipyz, + "use_colors" : option_colors, + "use_lights" : option_lights, + "use_cameras" : option_cameras, + "embed_meshes" : option_embed_meshes, + "base_html" : option_url_base_html, + "copy_textures": option_copy_textures } scene_text += generate_ascii_scene(data) @@ -1539,7 +1672,11 @@ def save(operator, context, filepath = "", option_lights = False, option_cameras = False, option_scale = 1.0, - option_embed_meshes = True): + option_embed_meshes = True, + option_url_base_html = False, + option_copy_textures = False): + + #print("URL TYPE", option_url_base_html) filepath = ensure_extension(filepath, '.js') @@ -1568,9 +1705,9 @@ def save(operator, context, filepath = "", name = obj.data.name if name not in geo_set: - + if option_embed_meshes: - + text, model_string = generate_mesh_string(obj, scene, option_vertices, option_vertices_truncate, @@ -1580,17 +1717,20 @@ def save(operator, context, filepath = "", option_uv_coords, option_materials, option_colors, - False, + False, # align_model option_flip_yz, option_scale, - False) - + False, # export_single_model + False, # option_copy_textures + filepath) + embeds[name] = model_string else: fname = generate_mesh_filename(name, filepath) - export_mesh(obj, scene, fname, + export_mesh(obj, scene, + fname, option_vertices, option_vertices_truncate, option_faces, @@ -1599,14 +1739,23 @@ def save(operator, context, filepath = "", option_uv_coords, option_materials, option_colors, - False, + False, # align_model option_flip_yz, option_scale, - False) + False, # export_single_model + option_copy_textures) geo_set.add(name) - export_scene(scene, filepath, option_flip_yz, option_colors, option_lights, option_cameras, option_embed_meshes, embeds) + export_scene(scene, filepath, + option_flip_yz, + option_colors, + option_lights, + option_cameras, + option_embed_meshes, + embeds, + option_url_base_html, + option_copy_textures) else: @@ -1626,7 +1775,7 @@ def save(operator, context, filepath = "", align_model, option_flip_yz, option_scale, - True) - + True, # export_single_model + option_copy_textures) return {'FINISHED'} -- GitLab