Преглед на файлове

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.
alteredq преди 14 години
родител
ревизия
ed92022b4b

+ 23 - 7
utils/exporters/blender/2.58/scripts/addons/io_mesh_threejs/__init__.py

@@ -20,13 +20,11 @@
 # Init
 # Init
 # ################################################################
 # ################################################################
 
 
-# To support reload properly, try to access a package var,
-# if it's there, reload everything
 
 
 bl_info = {
 bl_info = {
     "name": "three.js format",
     "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),
     "blender": (2, 5, 7),
     "api": 35622,
     "api": 35622,
     "location": "File > Import-Export",
     "location": "File > Import-Export",
@@ -36,6 +34,11 @@ bl_info = {
     "tracker_url": "https://github.com/mrdoob/three.js/issues",
     "tracker_url": "https://github.com/mrdoob/three.js/issues",
     "category": "Import-Export"}
     "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():
 if "bpy" in locals():
     import imp
     import imp
     if "export_threejs" in locals():
     if "export_threejs" in locals():
@@ -43,7 +46,6 @@ if "bpy" in locals():
     if "import_threejs" in locals():
     if "import_threejs" in locals():
         imp.reload(import_threejs)
         imp.reload(import_threejs)
 
 
-import bpy
 from bpy.props import *
 from bpy.props import *
 from bpy_extras.io_utils import ExportHelper, ImportHelper
 from bpy_extras.io_utils import ExportHelper, ImportHelper
 
 
@@ -175,7 +177,9 @@ def save_settings_export(properties):
     settings = {
     settings = {
     "option_export_scene" : properties.option_export_scene,
     "option_export_scene" : properties.option_export_scene,
     "option_embed_meshes" : properties.option_embed_meshes,
     "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_lights" : properties.option_lights,
     "option_cameras" : properties.option_cameras,
     "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_export_scene = settings.get("option_export_scene", False)
     properties.option_embed_meshes = settings.get("option_embed_meshes", True)
     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_lights = settings.get("option_lights", False)
     properties.option_cameras = settings.get("option_cameras", 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_export_scene = BoolProperty(name = "Scene", description = "Export scene", default = False)
     option_embed_meshes = BoolProperty(name = "Embed", description = "Embed meshes", default = True)
     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_lights = BoolProperty(name = "Lights", description = "Export default scene lights", default = False)
     option_cameras = BoolProperty(name = "Cameras", description = "Export default scene cameras", 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)
         save_settings_export(self.properties)
 
 
         filepath = self.filepath
         filepath = self.filepath
+
         import io_mesh_threejs.export_threejs
         import io_mesh_threejs.export_threejs
         return io_mesh_threejs.export_threejs.save(self, context, **self.properties)
         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 = layout.row()
         row.prop(self.properties, "option_export_scene")
         row.prop(self.properties, "option_export_scene")
+
+        row = layout.row()
         row.prop(self.properties, "option_lights")
         row.prop(self.properties, "option_lights")
         row.prop(self.properties, "option_cameras")
         row.prop(self.properties, "option_cameras")
 
 
         row = layout.row()
         row = layout.row()
         row.prop(self.properties, "option_embed_meshes")
         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()
         layout.separator()
 
 
 
 

+ 235 - 86
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).
 Blender exporter for Three.js (ASCII JSON format).
 
 
 TODO
 TODO
-    - copy used images to folder where exported file goes
     - binary format
     - binary format
 """
 """
 
 
 import bpy
 import bpy
 import mathutils
 import mathutils
 
 
+import shutil
 import os
 import os
 import os.path
 import os.path
 import math
 import math
@@ -79,7 +79,7 @@ COLORS = [0xeeeeee, 0xee0000, 0x00ee00, 0x0000ee, 0xeeee00, 0x00eeee, 0xee00ee]
 TEMPLATE_SCENE_ASCII = """\
 TEMPLATE_SCENE_ASCII = """\
 /* Converted from: %(fname)s
 /* 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/
  * https://github.com/mrdoob/three.js/tree/master/utils/exporters/blender/
  *
  *
  * objects:    %(nobjects)s
  * objects:    %(nobjects)s
@@ -91,7 +91,7 @@ TEMPLATE_SCENE_ASCII = """\
 var scene = {
 var scene = {
 
 
 "type" : "scene",
 "type" : "scene",
-"urlBaseType" : "relativeToScene",
+"urlBaseType" : %(basetype)s,
 
 
 %(sections)s
 %(sections)s
 
 
@@ -222,7 +222,7 @@ TEMPLATE_HEX = "0x%06x"
 
 
 TEMPLATE_FILE_ASCII = """\
 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/
  * https://github.com/mrdoob/three.js/tree/master/utils/exporters/blender/
  *
  *
  * vertices: %(nvertex)d
  * vertices: %(nvertex)d
@@ -693,6 +693,8 @@ def value2string(v):
         return '"%s"' % v
         return '"%s"' % v
     elif type(v) == bool:
     elif type(v) == bool:
         return str(v).lower()
         return str(v).lower()
+    elif type(v) == list:
+        return "[%s]" % (", ".join(value2string(x) for x in v))
     return str(v)
     return str(v)
 
 
 def generate_materials(mtl, materials, draw_type):
 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)
     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
     world = scene.world
 
 
     materials = {}
     materials = {}
@@ -749,22 +751,16 @@ def extract_materials(mesh, scene, option_colors):
             material['transparency'] = m.alpha
             material['transparency'] = m.alpha
 
 
             # not sure about mapping values to Blinn-Phong shader
             # 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
             # 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
             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
             material["vertexColors"] = m.THREE_useVertexColors and option_colors
 
 
@@ -779,7 +775,7 @@ def extract_materials(mesh, scene, option_colors):
 
 
     return materials
     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
     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
     # 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)
     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
 # ASCII model generator
 # #####################################################
 # #####################################################
@@ -821,7 +852,9 @@ def generate_ascii_model(mesh, scene,
                          align_model,
                          align_model,
                          flipyz,
                          flipyz,
                          option_scale,
                          option_scale,
-                         draw_type):
+                         draw_type,
+                         option_copy_textures,
+                         filepath):
 
 
     vertices = mesh.vertices[:]
     vertices = mesh.vertices[:]
 
 
@@ -843,7 +876,7 @@ def generate_ascii_model(mesh, scene,
     nedges = 0
     nedges = 0
 
 
     if option_materials:
     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:
     if option_edges:
         nedges = len(mesh.edges)
         nedges = len(mesh.edges)
@@ -873,7 +906,7 @@ def generate_ascii_model(mesh, scene,
     "ncolor"    : ncolor,
     "ncolor"    : ncolor,
     "nmaterial" : nmaterial,
     "nmaterial" : nmaterial,
     "nedges"    : nedges,
     "nedges"    : nedges,
-    
+
     "model"     : model_string
     "model"     : model_string
     }
     }
 
 
@@ -897,7 +930,9 @@ def generate_mesh_string(obj, scene,
                 align_model,
                 align_model,
                 flipyz,
                 flipyz,
                 option_scale,
                 option_scale,
-                export_single_model):
+                export_single_model,
+                option_copy_textures,
+                filepath):
 
 
     # collapse modifiers into mesh
     # collapse modifiers into mesh
 
 
@@ -937,6 +972,10 @@ def generate_mesh_string(obj, scene,
         if not active_col_layer:
         if not active_col_layer:
             option_colors = False
             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,
     text, model_string = generate_ascii_model(mesh, scene,
                                 option_vertices,
                                 option_vertices,
                                 option_vertices_truncate,
                                 option_vertices_truncate,
@@ -949,11 +988,13 @@ def generate_mesh_string(obj, scene,
                                 align_model,
                                 align_model,
                                 flipyz,
                                 flipyz,
                                 option_scale,
                                 option_scale,
-                                obj.draw_type)
+                                obj.draw_type,
+                                option_copy_textures_model,
+                                filepath)
     # remove temp mesh
     # remove temp mesh
 
 
     bpy.data.meshes.remove(mesh)
     bpy.data.meshes.remove(mesh)
-    
+
     return text, model_string
     return text, model_string
 
 
 def export_mesh(obj, scene, filepath,
 def export_mesh(obj, scene, filepath,
@@ -968,7 +1009,8 @@ def export_mesh(obj, scene, filepath,
                 align_model,
                 align_model,
                 flipyz,
                 flipyz,
                 option_scale,
                 option_scale,
-                export_single_model):
+                export_single_model,
+                option_copy_textures):
 
 
     """Export single mesh"""
     """Export single mesh"""
 
 
@@ -984,7 +1026,9 @@ def export_mesh(obj, scene, filepath,
                 align_model,
                 align_model,
                 flipyz,
                 flipyz,
                 option_scale,
                 option_scale,
-                export_single_model)
+                export_single_model,
+                option_copy_textures,
+                filepath)
 
 
     write_file(filepath, text)
     write_file(filepath, text)
 
 
@@ -1085,7 +1129,7 @@ def generate_objects(data):
                 visible = False
                 visible = False
 
 
             geometry_string = generate_string(geometry_id)
             geometry_string = generate_string(geometry_id)
-                
+
             object_string = TEMPLATE_OBJECT % {
             object_string = TEMPLATE_OBJECT % {
             "object_id"   : generate_string(object_id),
             "object_id"   : generate_string(object_id),
             "geometry_id" : geometry_string,
             "geometry_id" : geometry_string,
@@ -1103,7 +1147,7 @@ def generate_objects(data):
             "visible"      : generate_bool_property(visible)
             "visible"      : generate_bool_property(visible)
             }
             }
             chunks.append(object_string)
             chunks.append(object_string)
-            
+
         elif obj.type == "EMPTY" or (obj.type == "MESH" and not obj.THREE_exportGeometry):
         elif obj.type == "EMPTY" or (obj.type == "MESH" and not obj.THREE_exportGeometry):
 
 
             object_id = obj.name
             object_id = obj.name
@@ -1117,7 +1161,7 @@ def generate_objects(data):
                 group_string = generate_string_list(group_ids)
                 group_string = generate_string_list(group_ids)
 
 
             triggerType = obj.THREE_triggerType
             triggerType = obj.THREE_triggerType
-                
+
             object_string = TEMPLATE_EMPTY % {
             object_string = TEMPLATE_EMPTY % {
             "object_id"   : generate_string(object_id),
             "object_id"   : generate_string(object_id),
             "group_id"    : group_string,
             "group_id"    : group_string,
@@ -1153,7 +1197,7 @@ def generate_geometries(data):
             if name not in geo_set:
             if name not in geo_set:
 
 
                 geometry_id = "geo_%s" % name
                 geometry_id = "geo_%s" % name
-                
+
                 if data["embed_meshes"]:
                 if data["embed_meshes"]:
 
 
                     embed_id = "emb_%s" % name
                     embed_id = "emb_%s" % name
@@ -1195,10 +1239,25 @@ def generate_textures_scene(data):
 
 
             texture_id = img.name
             texture_id = img.name
             texture_file = extract_texture_filename(img)
             texture_file = extract_texture_filename(img)
-            
+
+            if data["copy_textures"]:
+                save_image(img, texture_file, data["filepath"])
+
             extras = ""
             extras = ""
+
             if texture.repeat_x != 1 or texture.repeat_y != 1:
             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_string = TEMPLATE_TEXTURE % {
             "texture_id"   : generate_string(texture_id),
             "texture_id"   : generate_string(texture_id),
@@ -1215,6 +1274,19 @@ def extract_texture_filename(image):
     fn_strip = os.path.basename(fn)
     fn_strip = os.path.basename(fn)
     return fn_strip
     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
 # Scene exporter - materials
 # #####################################################
 # #####################################################
@@ -1248,38 +1320,60 @@ def extract_material_data(m, option_colors):
 
 
     material["specularCoef"] = m.specular_hardness
     material["specularCoef"] = m.specular_hardness
 
 
+    material["vertexColors"] = m.THREE_useVertexColors and option_colors
+
     material['mapDiffuse'] = ""
     material['mapDiffuse'] = ""
     material['mapLight'] = ""
     material['mapLight'] = ""
     material['mapNormal'] = ""
     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
     # 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:
                 else:
-
-                    if not material['mapDiffuse']:
-                        material['mapDiffuse'] = name
+                    if not textures['diffuse']:
+                        textures['diffuse'] = { "texture": texture, "slot": slot }
 
 
                     else:
                     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
                     break
 
 
-    material['shading'] = m.THREE_materialType
-
-    return material
+    return textures
 
 
 def generate_material_string(material):
 def generate_material_string(material):
     type_map = {
     type_map = {
@@ -1302,6 +1396,7 @@ def generate_material_string(material):
     colorMap = material['mapDiffuse']
     colorMap = material['mapDiffuse']
     lightMap = material['mapLight']
     lightMap = material['mapLight']
     normalMap = material['mapNormal']
     normalMap = material['mapNormal']
+    normalMapFactor = material['mapNormalFactor']
 
 
     if colorMap:
     if colorMap:
         parameters += ', "map": %s' % generate_string(colorMap)
         parameters += ', "map": %s' % generate_string(colorMap)
@@ -1309,10 +1404,12 @@ def generate_material_string(material):
         parameters += ', "lightMap": %s' % generate_string(lightMap)
         parameters += ', "lightMap": %s' % generate_string(lightMap)
     if normalMap:
     if normalMap:
         parameters += ', "normalMap": %s' % generate_string(normalMap)
         parameters += ', "normalMap": %s' % generate_string(normalMap)
+    if normalMapFactor != 1.0:
+        parameters += ', "normalMapFactor": %f' % normalMapFactor
 
 
     if material['vertexColors']:
     if material['vertexColors']:
         parameters += ', "vertexColors": "vertex"'
         parameters += ', "vertexColors": "vertex"'
-        
+
     material_string = TEMPLATE_MATERIAL_SCENE % {
     material_string = TEMPLATE_MATERIAL_SCENE % {
     "material_id" : generate_string(material_id),
     "material_id" : generate_string(material_id),
     "type"        : generate_string(material_type),
     "type"        : generate_string(material_type),
@@ -1340,14 +1437,13 @@ def generate_materials_scene(data):
 def generate_cameras(data):
 def generate_cameras(data):
     if data["use_cameras"]:
     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 = []
         chunks = []
 
 
-        for camera in cameras:
+        if not cams:
+            camera = DEFAULTS["camera"]
 
 
             if camera["type"] == "perspective":
             if camera["type"] == "perspective":
 
 
@@ -1377,6 +1473,29 @@ def generate_cameras(data):
 
 
             chunks.append(camera_string)
             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 ",\n\n".join(chunks)
 
 
     return ""
     return ""
@@ -1417,7 +1536,7 @@ def generate_lights(data):
             chunks.append(light_string)
             chunks.append(light_string)
 
 
         return ",\n\n".join(chunks)
         return ",\n\n".join(chunks)
-        
+
     return ""
     return ""
 
 
 # #####################################################
 # #####################################################
@@ -1425,20 +1544,20 @@ def generate_lights(data):
 # #####################################################
 # #####################################################
 
 
 def generate_embeds(data):
 def generate_embeds(data):
-    
+
     if data["embed_meshes"]:
     if data["embed_meshes"]:
 
 
         chunks = []
         chunks = []
-        
+
         for e in data["embeds"]:
         for e in data["embeds"]:
-            
+
             embed = '"emb_%s": {%s}' % (e, data["embeds"][e])
             embed = '"emb_%s": {%s}' % (e, data["embeds"][e])
             chunks.append(embed)
             chunks.append(embed)
-            
+
         return ",\n\n".join(chunks)
         return ",\n\n".join(chunks)
 
 
     return ""
     return ""
-    
+
 # #####################################################
 # #####################################################
 # Scene exporter - generate ASCII scene
 # Scene exporter - generate ASCII scene
 # #####################################################
 # #####################################################
@@ -1455,6 +1574,13 @@ def generate_ascii_scene(data):
 
 
     embeds = generate_embeds(data)
     embeds = generate_embeds(data)
 
 
+    basetype = "relativeTo"
+
+    if data["base_html"]:
+        basetype += "HTML"
+    else:
+        basetype += "Scene"
+
     sections = [
     sections = [
     ["objects",    objects],
     ["objects",    objects],
     ["geometries", geometries],
     ["geometries", geometries],
@@ -1474,7 +1600,11 @@ def generate_ascii_scene(data):
 
 
     default_camera = ""
     default_camera = ""
     if data["use_cameras"]:
     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 = {
     parameters = {
     "fname"     : data["source_file"],
     "fname"     : data["source_file"],
@@ -1488,6 +1618,7 @@ def generate_ascii_scene(data):
     "nobjects"      : nobjects,
     "nobjects"      : nobjects,
     "ngeometries"   : ngeometries,
     "ngeometries"   : ngeometries,
     "ntextures"     : ntextures,
     "ntextures"     : ntextures,
+    "basetype"      : generate_string(basetype),
     "nmaterials"    : nmaterials,
     "nmaterials"    : nmaterials,
 
 
     "position"      : generate_vec3(DEFAULTS["position"]),
     "position"      : generate_vec3(DEFAULTS["position"]),
@@ -1499,22 +1630,24 @@ def generate_ascii_scene(data):
 
 
     return text
     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)
     source_file = os.path.basename(bpy.data.filepath)
 
 
     scene_text = ""
     scene_text = ""
     data = {
     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)
     scene_text += generate_ascii_scene(data)
 
 
@@ -1539,7 +1672,11 @@ def save(operator, context, filepath = "",
          option_lights = False,
          option_lights = False,
          option_cameras = False,
          option_cameras = False,
          option_scale = 1.0,
          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')
     filepath = ensure_extension(filepath, '.js')
 
 
@@ -1568,9 +1705,9 @@ def save(operator, context, filepath = "",
                     name = obj.data.name
                     name = obj.data.name
 
 
                 if name not in geo_set:
                 if name not in geo_set:
-                    
+
                     if option_embed_meshes:
                     if option_embed_meshes:
-                        
+
                         text, model_string = generate_mesh_string(obj, scene,
                         text, model_string = generate_mesh_string(obj, scene,
                                                         option_vertices,
                                                         option_vertices,
                                                         option_vertices_truncate,
                                                         option_vertices_truncate,
@@ -1580,17 +1717,20 @@ def save(operator, context, filepath = "",
                                                         option_uv_coords,
                                                         option_uv_coords,
                                                         option_materials,
                                                         option_materials,
                                                         option_colors,
                                                         option_colors,
-                                                        False,
+                                                        False,          # align_model
                                                         option_flip_yz,
                                                         option_flip_yz,
                                                         option_scale,
                                                         option_scale,
-                                                        False)
-                        
+                                                        False,          # export_single_model
+                                                        False,          # option_copy_textures
+                                                        filepath)
+
                         embeds[name] = model_string
                         embeds[name] = model_string
 
 
                     else:
                     else:
 
 
                         fname = generate_mesh_filename(name, filepath)
                         fname = generate_mesh_filename(name, filepath)
-                        export_mesh(obj, scene, fname,
+                        export_mesh(obj, scene,
+                                    fname,
                                     option_vertices,
                                     option_vertices,
                                     option_vertices_truncate,
                                     option_vertices_truncate,
                                     option_faces,
                                     option_faces,
@@ -1599,14 +1739,23 @@ def save(operator, context, filepath = "",
                                     option_uv_coords,
                                     option_uv_coords,
                                     option_materials,
                                     option_materials,
                                     option_colors,
                                     option_colors,
-                                    False,
+                                    False,          # align_model
                                     option_flip_yz,
                                     option_flip_yz,
                                     option_scale,
                                     option_scale,
-                                    False)
+                                    False,          # export_single_model
+                                    option_copy_textures)
 
 
                     geo_set.add(name)
                     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:
     else:
 
 
@@ -1626,7 +1775,7 @@ def save(operator, context, filepath = "",
                     align_model,
                     align_model,
                     option_flip_yz,
                     option_flip_yz,
                     option_scale,
                     option_scale,
-                    True)
-
+                    True,            # export_single_model
+                    option_copy_textures)
 
 
     return {'FINISHED'}
     return {'FINISHED'}