mirror of
https://github.com/wahyd4/three.js.git
synced 2026-08-09 04:56:01 +10:00
Blender 2.57 support
This commit is contained in:
@@ -0,0 +1,374 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# ################################################################
|
||||
# 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",
|
||||
"version": (1, 0),
|
||||
"blender": (2, 5, 7),
|
||||
"api": 35622,
|
||||
"location": "File > Import-Export > three.js",
|
||||
"description": "Import-Export THREEjs meshes",
|
||||
"warning": "",
|
||||
"tracker_url": "",
|
||||
"support": 'OFFICIAL',
|
||||
"category": "Import-Export"}
|
||||
|
||||
if "bpy" in locals():
|
||||
import imp
|
||||
if "export_threejs" in locals():
|
||||
imp.reload(export_threejs)
|
||||
if "import_threejs" in locals():
|
||||
imp.reload(import_threejs)
|
||||
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
from io_utils import ExportHelper, ImportHelper
|
||||
|
||||
# ################################################################
|
||||
# Custom properties
|
||||
# ################################################################
|
||||
|
||||
bpy.types.Object.THREE_castsShadow = bpy.props.BoolProperty()
|
||||
bpy.types.Object.THREE_meshCollider = bpy.props.BoolProperty()
|
||||
bpy.types.Object.THREE_exportGeometry = bpy.props.BoolProperty(default = True)
|
||||
|
||||
THREE_trigger_types = [("None", "None", "None"), ("Small", "Small", "Small"), ("Large", "Large", "Large")]
|
||||
bpy.types.Object.THREE_triggerType = EnumProperty(name = "Trigger type", description = "Trigger type", items = THREE_trigger_types, default = "None")
|
||||
|
||||
bpy.types.Material.THREE_useVertexColors = bpy.props.BoolProperty()
|
||||
|
||||
THREE_material_types = [("Basic", "Basic", "Basic"), ("Phong", "Phong", "Phong"), ("Lambert", "Lambert", "Lambert")]
|
||||
bpy.types.Material.THREE_materialType = EnumProperty(name = "Material type", description = "Material type", items = THREE_material_types, default = "Lambert")
|
||||
|
||||
class OBJECT_PT_hello( bpy.types.Panel ):
|
||||
|
||||
bl_label = "THREE"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "object"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
obj = context.object
|
||||
|
||||
row = layout.row()
|
||||
row.label(text="Selected object: " + obj.name )
|
||||
|
||||
row = layout.row()
|
||||
row.prop( obj, "THREE_exportGeometry", text="Export geometry" )
|
||||
|
||||
row = layout.row()
|
||||
row.prop( obj, "THREE_castsShadow", text="Casts shadow" )
|
||||
|
||||
row = layout.row()
|
||||
row.prop( obj, "THREE_meshCollider", text="Mesh collider" )
|
||||
|
||||
row = layout.row()
|
||||
row.prop( obj, "THREE_triggerType", text="Trigger type" )
|
||||
|
||||
|
||||
class MATERIAL_PT_hello( bpy.types.Panel ):
|
||||
|
||||
bl_label = "THREE"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "material"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
mat = context.material
|
||||
|
||||
row = layout.row()
|
||||
row.label(text="Selected material: " + mat.name )
|
||||
|
||||
row = layout.row()
|
||||
row.prop( mat, "THREE_materialType", text="Material type" )
|
||||
|
||||
row = layout.row()
|
||||
row.prop( mat, "THREE_useVertexColors", text="Use vertex colors" )
|
||||
|
||||
|
||||
# ################################################################
|
||||
# Importer
|
||||
# ################################################################
|
||||
|
||||
class ImportTHREEJS(bpy.types.Operator, ImportHelper):
|
||||
'''Load a Three.js ASCII JSON model'''
|
||||
|
||||
bl_idname = "import.threejs"
|
||||
bl_label = "Import Three.js"
|
||||
|
||||
filename_ext = ".js"
|
||||
filter_glob = StringProperty(default="*.js", options={'HIDDEN'})
|
||||
|
||||
option_flip_yz = BoolProperty(name="Flip YZ", description="Flip YZ", default=True)
|
||||
recalculate_normals = BoolProperty(name="Recalculate normals", description="Recalculate vertex normals", default=True)
|
||||
|
||||
def execute(self, context):
|
||||
import io_mesh_threejs.import_threejs
|
||||
return io_mesh_threejs.import_threejs.load(self, context, **self.properties)
|
||||
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
row = layout.row()
|
||||
row.prop(self.properties, "option_flip_yz")
|
||||
|
||||
row = layout.row()
|
||||
row.prop(self.properties, "recalculate_normals")
|
||||
|
||||
|
||||
# ################################################################
|
||||
# Exporter - settings
|
||||
# ################################################################
|
||||
|
||||
SETTINGS_FILE_EXPORT = "threejs_settings_export.js"
|
||||
|
||||
import os
|
||||
import json
|
||||
|
||||
def file_exists(filename):
|
||||
"""Return true if file exists and accessible for reading.
|
||||
|
||||
Should be safer than just testing for existence due to links and
|
||||
permissions magic on Unix filesystems.
|
||||
|
||||
@rtype: boolean
|
||||
"""
|
||||
|
||||
try:
|
||||
f = open(filename, 'r')
|
||||
f.close()
|
||||
return True
|
||||
except IOError:
|
||||
return False
|
||||
|
||||
def get_settings_fullpath():
|
||||
return os.path.join(bpy.app.tempdir, SETTINGS_FILE_EXPORT)
|
||||
|
||||
def save_settings_export(properties):
|
||||
|
||||
settings = {
|
||||
"option_export_scene" : properties.option_export_scene,
|
||||
"option_embed_meshes" : properties.option_embed_meshes,
|
||||
|
||||
"option_lights" : properties.option_lights,
|
||||
"option_cameras" : properties.option_cameras,
|
||||
|
||||
"option_flip_yz" : properties.option_flip_yz,
|
||||
|
||||
"option_materials" : properties.option_materials,
|
||||
"option_normals" : properties.option_normals,
|
||||
"option_colors" : properties.option_colors,
|
||||
"option_uv_coords" : properties.option_uv_coords,
|
||||
"option_edges" : properties.option_edges,
|
||||
"option_faces" : properties.option_faces,
|
||||
"option_vertices" : properties.option_vertices,
|
||||
|
||||
"option_vertices_truncate" : properties.option_vertices_truncate,
|
||||
"option_scale" : properties.option_scale,
|
||||
|
||||
"align_model" : properties.align_model
|
||||
}
|
||||
|
||||
fname = get_settings_fullpath()
|
||||
f = open(fname, "w")
|
||||
json.dump(settings, f)
|
||||
|
||||
def restore_settings_export(properties):
|
||||
|
||||
settings = {}
|
||||
|
||||
fname = get_settings_fullpath()
|
||||
if file_exists(fname):
|
||||
f = open(fname, "r")
|
||||
settings = json.load(f)
|
||||
|
||||
properties.option_vertices = settings.get("option_vertices", True)
|
||||
properties.option_vertices_truncate = settings.get("option_vertices_truncate", False)
|
||||
properties.option_faces = settings.get("option_faces", True)
|
||||
properties.option_normals = settings.get("option_normals", True)
|
||||
properties.option_edges = settings.get("option_edges", False)
|
||||
|
||||
properties.option_colors = settings.get("option_colors", True)
|
||||
properties.option_uv_coords = settings.get("option_uv_coords", True)
|
||||
properties.option_materials = settings.get("option_materials", True)
|
||||
|
||||
properties.align_model = settings.get("align_model", "None")
|
||||
|
||||
properties.option_scale = settings.get("option_scale", 1.0)
|
||||
properties.option_flip_yz = settings.get("option_flip_yz", True)
|
||||
|
||||
properties.option_export_scene = settings.get("option_export_scene", False)
|
||||
properties.option_embed_meshes = settings.get("option_embed_meshes", True)
|
||||
|
||||
properties.option_lights = settings.get("option_lights", False)
|
||||
properties.option_cameras = settings.get("option_cameras", False)
|
||||
|
||||
# ################################################################
|
||||
# Exporter
|
||||
# ################################################################
|
||||
|
||||
class ExportTHREEJS(bpy.types.Operator, ExportHelper):
|
||||
'''Export selected object / scene for Three.js (ASCII JSON format).'''
|
||||
|
||||
bl_idname = "export.threejs"
|
||||
bl_label = "Export Three.js"
|
||||
|
||||
filename_ext = ".js"
|
||||
|
||||
option_vertices = BoolProperty(name = "Vertices", description = "Export vertices", default = True)
|
||||
option_vertices_deltas = BoolProperty(name = "Deltas", description = "Delta vertices", default = False)
|
||||
option_vertices_truncate = BoolProperty(name = "Truncate", description = "Truncate vertices", default = False)
|
||||
|
||||
option_faces = BoolProperty(name = "Faces", description = "Export faces", default = True)
|
||||
option_faces_deltas = BoolProperty(name = "Deltas", description = "Delta faces", default = False)
|
||||
|
||||
option_normals = BoolProperty(name = "Normals", description = "Export normals", default = True)
|
||||
option_edges = BoolProperty(name = "Edges", description = "Export edges", default = False)
|
||||
|
||||
option_colors = BoolProperty(name = "Colors", description = "Export vertex colors", default = True)
|
||||
option_uv_coords = BoolProperty(name = "UVs", description = "Export texture coordinates", default = True)
|
||||
option_materials = BoolProperty(name = "Materials", description = "Export materials", default = True)
|
||||
|
||||
align_types = [("None","None","None"), ("Center","Center","Center"), ("Bottom","Bottom","Bottom"), ("Top","Top","Top")]
|
||||
align_model = EnumProperty(name = "Align model", description = "Align model", items = align_types, default = "None")
|
||||
|
||||
option_scale = FloatProperty(name = "Scale", description = "Scale vertices", min = 0.01, max = 1000.0, soft_min = 0.01, soft_max = 1000.0, default = 1.0)
|
||||
option_flip_yz = BoolProperty(name = "Flip YZ", description = "Flip YZ", default = True)
|
||||
|
||||
option_export_scene = BoolProperty(name = "Scene", description = "Export scene", default = False)
|
||||
option_embed_meshes = BoolProperty(name = "Embed", description = "Embed meshes", default = True)
|
||||
|
||||
option_lights = BoolProperty(name = "Lights", description = "Export default scene lights", default = False)
|
||||
option_cameras = BoolProperty(name = "Cameras", description = "Export default scene cameras", default = False)
|
||||
|
||||
def invoke(self, context, event):
|
||||
restore_settings_export(self.properties)
|
||||
return ExportHelper.invoke(self, context, event)
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.active_object != None
|
||||
|
||||
def execute(self, context):
|
||||
print("Selected: " + context.active_object.name)
|
||||
|
||||
if not self.properties.filepath:
|
||||
raise Exception("filename not set")
|
||||
|
||||
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)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
row = layout.row()
|
||||
row.label(text="Geometry:")
|
||||
|
||||
row = layout.row()
|
||||
row.prop(self.properties, "option_vertices")
|
||||
row = layout.row()
|
||||
row.enabled = self.properties.option_vertices
|
||||
# row.prop(self.properties, "option_vertices_deltas")
|
||||
row.prop(self.properties, "option_vertices_truncate")
|
||||
layout.separator()
|
||||
|
||||
row = layout.row()
|
||||
row.prop(self.properties, "option_faces")
|
||||
row = layout.row()
|
||||
row.enabled = self.properties.option_faces
|
||||
# row.prop(self.properties, "option_faces_deltas")
|
||||
layout.separator()
|
||||
|
||||
row = layout.row()
|
||||
row.prop(self.properties, "option_normals")
|
||||
layout.separator()
|
||||
|
||||
row = layout.row()
|
||||
row.prop(self.properties, "option_edges")
|
||||
layout.separator()
|
||||
|
||||
row = layout.row()
|
||||
row.label(text="Materials:")
|
||||
|
||||
row = layout.row()
|
||||
row.prop(self.properties, "option_uv_coords")
|
||||
row.prop(self.properties, "option_colors")
|
||||
row = layout.row()
|
||||
row.prop(self.properties, "option_materials")
|
||||
layout.separator()
|
||||
|
||||
row = layout.row()
|
||||
row.label(text="Settings:")
|
||||
|
||||
row = layout.row()
|
||||
row.prop(self.properties, "align_model")
|
||||
row = layout.row()
|
||||
row.prop(self.properties, "option_flip_yz")
|
||||
row.prop(self.properties, "option_scale")
|
||||
layout.separator()
|
||||
|
||||
row = layout.row()
|
||||
row.label(text="Beta:")
|
||||
|
||||
row = layout.row()
|
||||
row.prop(self.properties, "option_export_scene")
|
||||
row.prop(self.properties, "option_lights")
|
||||
row.prop(self.properties, "option_cameras")
|
||||
|
||||
row = layout.row()
|
||||
row.prop(self.properties, "option_embed_meshes")
|
||||
layout.separator()
|
||||
|
||||
|
||||
# ################################################################
|
||||
# Common
|
||||
# ################################################################
|
||||
|
||||
def menu_func_export(self, context):
|
||||
default_path = bpy.data.filepath.replace(".blend", ".js")
|
||||
self.layout.operator(ExportTHREEJS.bl_idname, text="Three.js (.js)").filepath = default_path
|
||||
|
||||
def menu_func_import(self, context):
|
||||
self.layout.operator(ImportTHREEJS.bl_idname, text="Three.js (.js)")
|
||||
|
||||
def register():
|
||||
bpy.utils.register_module(__name__)
|
||||
bpy.types.INFO_MT_file_export.append(menu_func_export)
|
||||
bpy.types.INFO_MT_file_import.append(menu_func_import)
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_module(__name__)
|
||||
bpy.types.INFO_MT_file_export.remove(menu_func_export)
|
||||
bpy.types.INFO_MT_file_import.remove(menu_func_import)
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,635 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# Based on import_obj.py
|
||||
# Contributors: alteredq
|
||||
|
||||
|
||||
"""
|
||||
Blender importer for Three.js (ASCII JSON format).
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
import bpy
|
||||
import mathutils
|
||||
from mathutils.geometry import tesselate_polygon
|
||||
from io_utils import load_image, unpack_list, unpack_face_list
|
||||
|
||||
# #####################################################
|
||||
# Generators
|
||||
# #####################################################
|
||||
def setColor(c, t):
|
||||
c.r = t[0]
|
||||
c.g = t[1]
|
||||
c.b = t[2]
|
||||
|
||||
def create_texture(filename, modelpath):
|
||||
name = filename
|
||||
texture = bpy.data.textures.new(name, type='IMAGE')
|
||||
|
||||
image = load_image(filename, modelpath)
|
||||
has_data = False
|
||||
|
||||
if image:
|
||||
texture.image = image
|
||||
has_data = image.has_data
|
||||
|
||||
return texture
|
||||
|
||||
def create_materials(data, modelpath):
|
||||
materials = []
|
||||
materials_data = data.get("materials", [])
|
||||
|
||||
for i, m in enumerate(materials_data):
|
||||
|
||||
name = m.get("DbgName", "material_%d" % i)
|
||||
|
||||
colorAmbient = m.get("colorAmbient", None)
|
||||
colorDiffuse = m.get("colorDiffuse", None)
|
||||
colorSpecular = m.get("colorSpecular", None)
|
||||
alpha = m.get("transparency", 1.0)
|
||||
specular_hardness = m.get("specularCoef", 0)
|
||||
|
||||
mapDiffuse = m.get("mapDiffuse", None)
|
||||
mapLightmap = m.get("mapLightmap", None)
|
||||
|
||||
vertexColorsType = m.get("vertexColors", False)
|
||||
|
||||
useVertexColors = False
|
||||
if vertexColorsType:
|
||||
useVertexColors = True
|
||||
|
||||
material = bpy.data.materials.new(name)
|
||||
|
||||
material.THREE_useVertexColors = useVertexColors
|
||||
|
||||
if colorDiffuse:
|
||||
setColor(material.diffuse_color, colorDiffuse)
|
||||
material.diffuse_intensity = 1.0
|
||||
|
||||
if colorSpecular:
|
||||
setColor(material.specular_color, colorSpecular)
|
||||
material.specular_intensity = 1.0
|
||||
|
||||
if alpha < 1.0:
|
||||
material.alpha = alpha
|
||||
material.use_transparency = True
|
||||
|
||||
if specular_hardness:
|
||||
material.specular_hardness = specular_hardness
|
||||
|
||||
if mapDiffuse:
|
||||
texture = create_texture(mapDiffuse, modelpath)
|
||||
mtex = material.texture_slots.add()
|
||||
mtex.texture = texture
|
||||
mtex.texture_coords = 'UV'
|
||||
mtex.use = True
|
||||
mtex.use_map_color_diffuse = True
|
||||
|
||||
material.active_texture = texture
|
||||
|
||||
materials.append(material)
|
||||
|
||||
return materials
|
||||
|
||||
def create_mesh_object(name, vertices, materials, face_data, flipYZ, recalculate_normals):
|
||||
|
||||
faces = face_data["faces"]
|
||||
vertexNormals = face_data["vertexNormals"]
|
||||
vertexColors = face_data["vertexColors"]
|
||||
vertexUVs = face_data["vertexUVs"]
|
||||
faceMaterials = face_data["materials"]
|
||||
faceColors = face_data["faceColors"]
|
||||
|
||||
edges = []
|
||||
|
||||
# Create a new mesh
|
||||
|
||||
me = bpy.data.meshes.new(name)
|
||||
me.from_pydata(vertices, edges, faces)
|
||||
|
||||
# Handle normals
|
||||
|
||||
if not recalculate_normals:
|
||||
me.update(calc_edges = True)
|
||||
|
||||
if face_data["hasVertexNormals"]:
|
||||
|
||||
print("setting vertex normals")
|
||||
|
||||
for fi in range(len(faces)):
|
||||
|
||||
if vertexNormals[fi]:
|
||||
|
||||
#print("setting face %i with %i vertices" % (fi, len(normals[fi])))
|
||||
|
||||
# if me.update() is called after setting vertex normals
|
||||
# setting face.use_smooth overrides these normals
|
||||
# - this fixes weird shading artefacts (seems to come from sharing
|
||||
# of vertices between faces, didn't find a way how to set vertex normals
|
||||
# per face use of vertex as opposed to per vertex),
|
||||
# - probably this just overrides all custom vertex normals
|
||||
# - to preserve vertex normals from the original data
|
||||
# call me.update() before setting them
|
||||
|
||||
me.faces[fi].use_smooth = True
|
||||
|
||||
if not recalculate_normals:
|
||||
for j in range(len(vertexNormals[fi])):
|
||||
|
||||
vertexNormal = vertexNormals[fi][j]
|
||||
|
||||
x = vertexNormal[0]
|
||||
y = vertexNormal[1]
|
||||
z = vertexNormal[2]
|
||||
|
||||
if flipYZ:
|
||||
tmp = y
|
||||
y = -z
|
||||
z = tmp
|
||||
|
||||
# flip normals (this make them look consistent with the original before export)
|
||||
|
||||
#x = -x
|
||||
#y = -y
|
||||
#z = -z
|
||||
|
||||
vi = me.faces[fi].vertices[j]
|
||||
|
||||
me.vertices[vi].normal.x = x
|
||||
me.vertices[vi].normal.y = y
|
||||
me.vertices[vi].normal.z = z
|
||||
|
||||
if recalculate_normals:
|
||||
me.update(calc_edges = True)
|
||||
|
||||
# Handle colors
|
||||
|
||||
if face_data["hasVertexColors"]:
|
||||
|
||||
print("setting vertex colors")
|
||||
|
||||
me.vertex_colors.new("vertex_color_layer_0")
|
||||
|
||||
for fi in range(len(faces)):
|
||||
|
||||
if vertexColors[fi]:
|
||||
|
||||
face_colors = me.vertex_colors[0].data[fi]
|
||||
face_colors = face_colors.color1, face_colors.color2, face_colors.color3, face_colors.color4
|
||||
|
||||
for vi in range(len(vertexColors[fi])):
|
||||
|
||||
r = vertexColors[fi][vi][0]
|
||||
g = vertexColors[fi][vi][1]
|
||||
b = vertexColors[fi][vi][2]
|
||||
|
||||
face_colors[vi].r = r
|
||||
face_colors[vi].g = g
|
||||
face_colors[vi].b = b
|
||||
|
||||
elif face_data["hasFaceColors"]:
|
||||
|
||||
print("setting vertex colors from face colors")
|
||||
|
||||
me.vertex_colors.new("vertex_color_layer_0")
|
||||
|
||||
for fi in range(len(faces)):
|
||||
|
||||
if faceColors[fi]:
|
||||
|
||||
r = faceColors[fi][0]
|
||||
g = faceColors[fi][1]
|
||||
b = faceColors[fi][2]
|
||||
|
||||
face_colors = me.vertex_colors[0].data[fi]
|
||||
face_colors = face_colors.color1, face_colors.color2, face_colors.color3, face_colors.color4
|
||||
|
||||
for vi in range(len(faces[fi])):
|
||||
|
||||
face_colors[vi].r = r
|
||||
face_colors[vi].g = g
|
||||
face_colors[vi].b = b
|
||||
|
||||
# Handle uvs
|
||||
|
||||
if face_data["hasVertexUVs"]:
|
||||
|
||||
print("setting vertex uvs")
|
||||
|
||||
for li, layer in enumerate(vertexUVs):
|
||||
|
||||
me.uv_textures.new("uv_layer_%d" % li)
|
||||
|
||||
for fi in range(len(faces)):
|
||||
|
||||
if layer[fi]:
|
||||
|
||||
uv_face = me.uv_textures[li].data[fi]
|
||||
face_uvs = uv_face.uv1, uv_face.uv2, uv_face.uv3, uv_face.uv4
|
||||
|
||||
for vi in range(len(layer[fi])):
|
||||
|
||||
u = layer[fi][vi][0]
|
||||
v = layer[fi][vi][1]
|
||||
|
||||
face_uvs[vi].x = u
|
||||
face_uvs[vi].y = 1.0 - v
|
||||
|
||||
active_texture = materials[faceMaterials[fi]].active_texture
|
||||
|
||||
if active_texture:
|
||||
uv_face.use_image = True
|
||||
uv_face.image = active_texture.image
|
||||
|
||||
|
||||
# Handle materials # 1
|
||||
|
||||
if face_data["hasMaterials"]:
|
||||
|
||||
|
||||
print("setting materials (mesh)")
|
||||
|
||||
for m in materials:
|
||||
|
||||
me.materials.append(m)
|
||||
|
||||
print("setting materials (faces)")
|
||||
|
||||
for fi in range(len(faces)):
|
||||
|
||||
if faceMaterials[fi] >= 0:
|
||||
|
||||
me.faces[fi].material_index = faceMaterials[fi]
|
||||
|
||||
# Create a new object
|
||||
|
||||
ob = bpy.data.objects.new(name, me)
|
||||
ob.data = me # link the mesh data to the object
|
||||
|
||||
|
||||
scene = bpy.context.scene # get the current scene
|
||||
scene.objects.link(ob) # link the object into the scene
|
||||
|
||||
ob.location = scene.cursor_location # position object at 3d-cursor
|
||||
|
||||
|
||||
# #####################################################
|
||||
# Faces
|
||||
# #####################################################
|
||||
|
||||
def extract_faces(data):
|
||||
|
||||
result = {
|
||||
"faces" : [],
|
||||
"materials" : [],
|
||||
"faceUVs" : [],
|
||||
"vertexUVs" : [],
|
||||
"faceNormals" : [],
|
||||
"vertexNormals" : [],
|
||||
"faceColors" : [],
|
||||
"vertexColors" : [],
|
||||
|
||||
"hasVertexNormals" : False,
|
||||
"hasVertexUVs" : False,
|
||||
"hasVertexColors" : False,
|
||||
"hasFaceColors" : False,
|
||||
"hasMaterials" : False
|
||||
}
|
||||
|
||||
faces = data.get("faces", [])
|
||||
normals = data.get("normals", [])
|
||||
colors = data.get("colors", [])
|
||||
|
||||
offset = 0
|
||||
zLength = len(faces)
|
||||
|
||||
# disregard empty arrays
|
||||
|
||||
nUvLayers = 0
|
||||
|
||||
for layer in data["uvs"]:
|
||||
|
||||
if len(layer) > 0:
|
||||
nUvLayers += 1
|
||||
result["faceUVs"].append([])
|
||||
result["vertexUVs"].append([])
|
||||
|
||||
|
||||
while ( offset < zLength ):
|
||||
|
||||
type = faces[ offset ]
|
||||
offset += 1
|
||||
|
||||
isQuad = isBitSet( type, 0 )
|
||||
hasMaterial = isBitSet( type, 1 )
|
||||
hasFaceUv = isBitSet( type, 2 )
|
||||
hasFaceVertexUv = isBitSet( type, 3 )
|
||||
hasFaceNormal = isBitSet( type, 4 )
|
||||
hasFaceVertexNormal = isBitSet( type, 5 )
|
||||
hasFaceColor = isBitSet( type, 6 )
|
||||
hasFaceVertexColor = isBitSet( type, 7 )
|
||||
|
||||
#print("type", type, "bits", isQuad, hasMaterial, hasFaceUv, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor)
|
||||
|
||||
result["hasVertexUVs"] = result["hasVertexUVs"] or hasFaceVertexUv
|
||||
result["hasVertexNormals"] = result["hasVertexNormals"] or hasFaceVertexNormal
|
||||
result["hasVertexColors"] = result["hasVertexColors"] or hasFaceVertexColor
|
||||
result["hasFaceColors"] = result["hasFaceColors"] or hasFaceColor
|
||||
result["hasMaterials"] = result["hasMaterials"] or hasMaterial
|
||||
|
||||
# vertices
|
||||
|
||||
if isQuad:
|
||||
|
||||
a = faces[ offset ]
|
||||
offset += 1
|
||||
|
||||
b = faces[ offset ]
|
||||
offset += 1
|
||||
|
||||
c = faces[ offset ]
|
||||
offset += 1
|
||||
|
||||
d = faces[ offset ]
|
||||
offset += 1
|
||||
|
||||
face = [a, b, c, d]
|
||||
|
||||
nVertices = 4
|
||||
|
||||
else:
|
||||
|
||||
a = faces[ offset ]
|
||||
offset += 1
|
||||
|
||||
b = faces[ offset ]
|
||||
offset += 1
|
||||
|
||||
c = faces[ offset ]
|
||||
offset += 1
|
||||
|
||||
face = [a, b, c]
|
||||
|
||||
nVertices = 3
|
||||
|
||||
result["faces"].append(face)
|
||||
|
||||
# material
|
||||
|
||||
if hasMaterial:
|
||||
|
||||
materialIndex = faces[ offset ]
|
||||
offset += 1
|
||||
|
||||
else:
|
||||
|
||||
materialIndex = -1
|
||||
|
||||
result["materials"].append(materialIndex)
|
||||
|
||||
# uvs
|
||||
|
||||
for i in range(nUvLayers):
|
||||
|
||||
faceUv = None
|
||||
|
||||
if hasFaceUv:
|
||||
|
||||
uvLayer = data["uvs"][ i ]
|
||||
|
||||
uvIndex = faces[ offset ]
|
||||
offset += 1
|
||||
|
||||
u = uvLayer[ uvIndex * 2 ]
|
||||
v = uvLayer[ uvIndex * 2 + 1 ]
|
||||
|
||||
faceUv = [u, v]
|
||||
|
||||
result["faceUVs"][i].append(faceUv)
|
||||
|
||||
|
||||
if hasFaceVertexUv:
|
||||
|
||||
uvLayer = data["uvs"][ i ]
|
||||
|
||||
vertexUvs = []
|
||||
|
||||
for j in range(nVertices):
|
||||
|
||||
uvIndex = faces[ offset ]
|
||||
offset += 1
|
||||
|
||||
u = uvLayer[ uvIndex * 2 ]
|
||||
v = uvLayer[ uvIndex * 2 + 1 ]
|
||||
|
||||
vertexUvs.append([u, v])
|
||||
|
||||
result["vertexUVs"][i].append(vertexUvs)
|
||||
|
||||
|
||||
if hasFaceNormal:
|
||||
|
||||
normalIndex = faces[ offset ] * 3
|
||||
offset += 1
|
||||
|
||||
x = normals[ normalIndex ]
|
||||
y = normals[ normalIndex + 1 ]
|
||||
z = normals[ normalIndex + 2 ]
|
||||
|
||||
faceNormal = [x, y, z]
|
||||
|
||||
else:
|
||||
|
||||
faceNormal = None
|
||||
|
||||
result["faceNormals"].append(faceNormal)
|
||||
|
||||
|
||||
if hasFaceVertexNormal:
|
||||
|
||||
vertexNormals = []
|
||||
|
||||
for j in range(nVertices):
|
||||
|
||||
normalIndex = faces[ offset ] * 3
|
||||
offset += 1
|
||||
|
||||
x = normals[ normalIndex ]
|
||||
y = normals[ normalIndex + 1 ]
|
||||
z = normals[ normalIndex + 2 ]
|
||||
|
||||
vertexNormals.append( [x, y, z] )
|
||||
|
||||
|
||||
else:
|
||||
|
||||
vertexNormals = None
|
||||
|
||||
result["vertexNormals"].append(vertexNormals)
|
||||
|
||||
|
||||
if hasFaceColor:
|
||||
|
||||
colorIndex = faces[ offset ]
|
||||
offset += 1
|
||||
|
||||
faceColor = hexToTuple( colors[ colorIndex ] )
|
||||
|
||||
else:
|
||||
|
||||
faceColor = None
|
||||
|
||||
result["faceColors"].append(faceColor)
|
||||
|
||||
|
||||
if hasFaceVertexColor:
|
||||
|
||||
vertexColors = []
|
||||
|
||||
for j in range(nVertices):
|
||||
|
||||
colorIndex = faces[ offset ]
|
||||
offset += 1
|
||||
|
||||
color = hexToTuple( colors[ colorIndex ] )
|
||||
vertexColors.append( color )
|
||||
|
||||
else:
|
||||
|
||||
vertexColors = None
|
||||
|
||||
result["vertexColors"].append(vertexColors)
|
||||
|
||||
|
||||
return result
|
||||
|
||||
# #####################################################
|
||||
# Utils
|
||||
# #####################################################
|
||||
|
||||
def hexToTuple( hexColor ):
|
||||
r = (( hexColor >> 16 ) & 0xff) / 255.0
|
||||
g = (( hexColor >> 8 ) & 0xff) / 255.0
|
||||
b = ( hexColor & 0xff) / 255.0
|
||||
return (r, g, b)
|
||||
|
||||
def isBitSet(value, position):
|
||||
return value & ( 1 << position )
|
||||
|
||||
def splitArray(data, chunkSize):
|
||||
result = []
|
||||
chunk = []
|
||||
for i in range(len(data)):
|
||||
if i > 0 and i % chunkSize == 0:
|
||||
result.append(chunk)
|
||||
chunk = []
|
||||
chunk.append(data[i])
|
||||
result.append(chunk)
|
||||
return result
|
||||
|
||||
|
||||
def extract_json_string(text):
|
||||
marker_begin = "var model ="
|
||||
marker_end = "postMessage"
|
||||
|
||||
start = text.find(marker_begin) + len(marker_begin)
|
||||
end = text.find(marker_end)
|
||||
end = text.rfind("}", start, end)
|
||||
return text[start:end+1].strip()
|
||||
|
||||
def get_name(filepath):
|
||||
return os.path.splitext(os.path.basename(filepath))[0]
|
||||
|
||||
def get_path(filepath):
|
||||
return os.path.dirname(filepath)
|
||||
|
||||
# #####################################################
|
||||
# Parser
|
||||
# #####################################################
|
||||
|
||||
def load(operator, context, filepath, option_flip_yz = True, recalculate_normals = True):
|
||||
|
||||
print('\nimporting %r' % filepath)
|
||||
|
||||
time_main = time.time()
|
||||
|
||||
print("\tparsing JSON file...")
|
||||
|
||||
time_sub = time.time()
|
||||
|
||||
file = open(filepath, 'rU')
|
||||
rawcontent = file.read()
|
||||
file.close()
|
||||
|
||||
json_string = extract_json_string(rawcontent)
|
||||
data = json.loads( json_string )
|
||||
|
||||
time_new = time.time()
|
||||
|
||||
print('parsing %.4f sec' % (time_new - time_sub))
|
||||
|
||||
time_sub = time_new
|
||||
|
||||
# flip YZ
|
||||
|
||||
vertices = splitArray(data["vertices"], 3)
|
||||
|
||||
if option_flip_yz:
|
||||
vertices[:] = [(v[0], -v[2], v[1]) for v in vertices]
|
||||
|
||||
# extract faces
|
||||
|
||||
face_data = extract_faces(data)
|
||||
|
||||
# deselect all
|
||||
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
|
||||
nfaces = len(face_data["faces"])
|
||||
nvertices = len(vertices)
|
||||
nnormals = len(data.get("normals", [])) / 3
|
||||
ncolors = len(data.get("colors", [])) / 3
|
||||
nuvs = len(data.get("uvs", [])) / 2
|
||||
nmaterials = len(data.get("materials", []))
|
||||
|
||||
print('\tbuilding geometry...\n\tfaces:%i, vertices:%i, vertex normals: %i, vertex uvs: %i, vertex colors: %i, materials: %i ...' % (
|
||||
nfaces, nvertices, nnormals, nuvs, ncolors, nmaterials ))
|
||||
|
||||
# Create materials
|
||||
|
||||
materials = create_materials(data, get_path(filepath))
|
||||
|
||||
# Create new obj
|
||||
|
||||
create_mesh_object(get_name(filepath), vertices, materials, face_data, option_flip_yz, recalculate_normals)
|
||||
|
||||
scene = bpy.context.scene
|
||||
scene.update()
|
||||
|
||||
time_new = time.time()
|
||||
|
||||
print('finished importing: %r in %.4f sec.' % (filepath, (time_new - time_main)))
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
||||
Reference in New Issue
Block a user