Skip to content
Snippets Groups Projects
Commit 9656588e authored by Ken Nign's avatar Ken Nign
Browse files

add absolute morph and edge weighting (crease) support.

safely create new edges.
trailing whitespace cleanup.
parent a668f0dc
Branches
Tags
No related merge requests found
......@@ -21,7 +21,7 @@
bl_addon_info= {
"name": "Import LightWave Objects",
"author": "Ken Nign (Ken9)",
"version": (1,0),
"version": (1, 2),
"blender": (2, 5, 3),
"api": 31744,
"location": "File > Import > LightWave Object (.lwo)",
......@@ -35,7 +35,7 @@ bl_addon_info= {
# Copyright (c) Ken Nign 2010
# ken@virginpi.com
#
# Version 1.0 - Sep 1, 2010
# Version 1.2 - Sep 7, 2010
#
# Loads a LightWave .lwo object file, including the vertex maps such as
# UV, Morph, Color and Weight maps.
......@@ -50,9 +50,16 @@ bl_addon_info= {
# NGons, polygons with more than 4 points are supported, but are
# added (as triangles) after the vertex maps have been applied. Thus they
# won't contain all the vertex data that the original ngon had.
#
#
# Blender is limited to only 8 UV Texture and 8 Vertex Color maps,
# thus only the first 8 of each can be imported.
#
# History:
#
# 1.2 Added Absolute Morph and CC Edge Weight support.
# Made edge creation safer.
# 1.0 First Release
import os
import io
......@@ -80,6 +87,7 @@ class _obj_layer(object):
"colmaps",
"uvmaps",
"morphs",
"edge_weights",
"surf_tags",
"has_subds",
)
......@@ -87,7 +95,7 @@ class _obj_layer(object):
self.name= ""
self.index= -1
self.parent_index= -1
self.pivot= [0,0,0]
self.pivot= [0, 0, 0]
self.pols= []
self.bones= []
self.bone_names= {}
......@@ -97,6 +105,7 @@ class _obj_layer(object):
self.colmaps= {}
self.uvmaps= {}
self.morphs= {}
self.edge_weights= {}
self.surf_tags= {}
self.has_subds= False
......@@ -141,38 +150,39 @@ class _obj_surf(object):
def load_lwo(filename,
context,
ADD_SUBD_MOD= True,
LOAD_HIDDEN= False,
SKEL_TO_ARM= True):
ADD_SUBD_MOD=True,
LOAD_HIDDEN=False,
SKEL_TO_ARM=True):
'''Read the LWO file, hand off to version specific function.'''
name, ext= os.path.splitext(os.path.basename(filename))
file= open(filename, 'rb')
try:
header, chunk_size, chunk_name = struct.unpack(">4s1L4s", file.read(12))
except:
print("Error parsing file header!")
file.close()
return
layers= []
surfs= {}
tags= []
# Gather the object data using the version specific handler.
if chunk_name == b'LWO2':
read_lwo2(file, filename, layers, surfs, tags, ADD_SUBD_MOD, LOAD_HIDDEN, SKEL_TO_ARM)
elif chunk_name == b'LWOB' or chunk_name == b'LWLO': # LWLO is a layered object.
elif chunk_name == b'LWOB' or chunk_name == b'LWLO':
# LWOB and LWLO are the old format, LWLO is a layered object.
read_lwob(file, filename, layers, surfs, tags, ADD_SUBD_MOD)
else:
print("Not a supported file type!")
file.close()
return
file.close()
# With the data gathered, build the object(s).
build_objects(layers, surfs, tags, name, ADD_SUBD_MOD, SKEL_TO_ARM)
layers= None
surfs.clear()
tags= None
......@@ -184,13 +194,13 @@ def read_lwo2(file, filename, layers, surfs, tags, add_subd_mod, load_hidden, sk
last_pols_count= 0
just_read_bones= False
print("Importing LWO: " + filename + "\nLWO v2 Format")
while True:
try:
rootchunk = chunk.Chunk(file)
except EOFError:
break
if rootchunk.chunkname == b'TAGS':
read_tags(rootchunk.read(), tags)
elif rootchunk.chunkname == b'LAYR':
......@@ -199,33 +209,39 @@ def read_lwo2(file, filename, layers, surfs, tags, add_subd_mod, load_hidden, sk
read_pnts(rootchunk.read(), layers)
elif rootchunk.chunkname == b'VMAP' and handle_layer:
vmap_type = rootchunk.read(4)
if vmap_type == b'WGHT':
read_weightmap(rootchunk.read(), layers)
elif vmap_type == b'MORF':
read_morph(rootchunk.read(), layers)
read_morph(rootchunk.read(), layers, False)
elif vmap_type == b'SPOT':
read_morph(rootchunk.read(), layers, True)
elif vmap_type == b'TXUV':
read_uvmap(rootchunk.read(), layers)
elif vmap_type == b'RGB ' or vmap_type == b'RGBA':
read_colmap(rootchunk.read(), layers)
else:
rootchunk.skip()
elif rootchunk.chunkname == b'VMAD' and handle_layer:
vmad_type= rootchunk.read(4)
if vmad_type == b'TXUV':
read_uv_vmad(rootchunk.read(), layers, last_pols_count)
elif vmad_type == b'RGB ' or vmad_type == b'RGBA':
read_color_vmad(rootchunk.read(), layers, last_pols_count)
elif vmad_type == b'WGHT':
# We only read the Edge Weight map if it's there.
read_weight_vmad(rootchunk.read(), layers)
else:
rootchunk.skip()
elif rootchunk.chunkname == b'POLS' and handle_layer:
face_type = rootchunk.read(4)
just_read_bones= False
# PTCH is LW's Subpatches, SUBD is CatmullClark.
if (face_type == b'FACE' or face_type == b'PTCH' or face_type == b'SUBD') and handle_layer:
if (face_type == b'FACE' or face_type == b'PTCH' or
face_type == b'SUBD') and handle_layer:
last_pols_count= read_pols(rootchunk.read(), layers)
if face_type != b'FACE':
layers[-1].has_subds= True
......@@ -234,13 +250,13 @@ def read_lwo2(file, filename, layers, surfs, tags, add_subd_mod, load_hidden, sk
just_read_bones= True
else:
rootchunk.skip()
elif rootchunk.chunkname == b'PTAG' and handle_layer:
tag_type,= struct.unpack("4s", rootchunk.read(4));
tag_type,= struct.unpack("4s", rootchunk.read(4))
if tag_type == b'SURF' and not just_read_bones:
# We have to ignore the surface data if we just read a bones chunk.
# Ignore the surface data if we just read a bones chunk.
read_surf_tags(rootchunk.read(), layers, last_pols_count)
elif skel_to_arm:
if tag_type == b'BNUP':
read_bone_tags(rootchunk.read(), layers, tags, 'BNUP')
......@@ -262,19 +278,20 @@ def read_lwob(file, filename, layers, surfs, tags, add_subd_mod):
'''Read version 1 file, LW < 6.'''
last_pols_count= 0
print("Importing LWO: " + filename + "\nLWO v1 Format")
while True:
try:
rootchunk = chunk.Chunk(file)
except EOFError:
break
if rootchunk.chunkname == b'SRFS':
read_tags(rootchunk.read(), tags)
elif rootchunk.chunkname == b'LAYR':
read_layr_5(rootchunk.read(), layers)
elif rootchunk.chunkname == b'PNTS':
if len(layers) == 0: # LWOB files have no LAYR chunk to set this up
if len(layers) == 0:
# LWOB files have no LAYR chunk to set this up.
nlayer= _obj_layer()
nlayer.name= "Layer 1"
layers.append(nlayer)
......@@ -285,7 +302,7 @@ def read_lwob(file, filename, layers, surfs, tags, add_subd_mod):
last_pols_count= read_pols_5(rootchunk.read(), layers)
layers[-1].has_subds= True
elif rootchunk.chunkname == b'PTAG':
tag_type,= struct.unpack("4s", rootchunk.read(4));
tag_type,= struct.unpack("4s", rootchunk.read(4))
if tag_type == b'SURF':
read_surf_tags_5(rootchunk.read(), layers, last_pols_count)
else:
......@@ -306,7 +323,7 @@ def read_lwostring(raw_name):
name_len = i + 1
if name_len % 2 == 1: # Test for oddness.
name_len += 1
if i > 0:
# Some plugins put non-text strings in the tags chunk.
name = raw_name[0:i].decode("utf-8", "ignore")
......@@ -315,7 +332,7 @@ def read_lwostring(raw_name):
return name, name_len
def read_vx(pointdata):
'''Read a variable-length index.'''
if pointdata[0] != 255:
......@@ -324,10 +341,10 @@ def read_vx(pointdata):
else:
index= pointdata[1]*65536 + pointdata[2]*256 + pointdata[3]
size= 4
return index, size
def read_tags(tag_bytes, object_tags):
'''Read the object's Tags chunk.'''
offset= 0
......@@ -343,25 +360,25 @@ def read_layr(layr_bytes, object_layers, load_hidden):
'''Read the object's layer data.'''
new_layr= _obj_layer()
new_layr.index, flags= struct.unpack(">HH", layr_bytes[0:4])
if flags > 0 and not load_hidden:
return False
print("Reading Object Layer")
offset= 4
new_layr.pivot= struct.unpack(">fff", layr_bytes[offset:offset+12])
offset+= 12
layr_name, name_len = read_lwostring(layr_bytes[offset:])
offset+= name_len
if layr_name:
new_layr.name= layr_name
else:
new_layr.name= "Layer %d" % (new_layr.index + 1)
if len(layr_bytes) == offset+2:
new_layr.parent_index,= struct.unpack(">h", layr_bytes[offset:offset+2])
object_layers.append(new_layr)
return True
......@@ -371,26 +388,26 @@ def read_layr_5(layr_bytes, object_layers):
# XXX: Need to check what these two exactly mean for a LWOB/LWLO file.
new_layr= _obj_layer()
new_layr.index, flags= struct.unpack(">HH", layr_bytes[0:4])
print("Reading Object Layer")
offset= 4
layr_name, name_len = read_lwostring(layr_bytes[offset:])
offset+= name_len
if name_len > 2 and layr_name != 'noname':
new_layr.name= layr_name
else:
new_layr.name= "Layer %d" % new_layr.index
object_layers.append(new_layr)
def read_pnts(pnt_bytes, object_layers):
'''Read the layer's points.'''
print("\tReading Layer ("+object_layers[-1].name+") Points")
offset= 0
chunk_len= len(pnt_bytes)
while offset < chunk_len:
pnts= struct.unpack(">fff", pnt_bytes[offset:offset+12])
offset+= 12
......@@ -408,34 +425,40 @@ def read_weightmap(weight_bytes, object_layers):
name, name_len= read_lwostring(weight_bytes[offset:])
offset+= name_len
weights= []
while offset < chunk_len:
pnt_id, pnt_id_len= read_vx(weight_bytes[offset:offset+4])
offset+= pnt_id_len
value,= struct.unpack(">f", weight_bytes[offset:offset+4])
offset+= 4
weights.append([pnt_id, value])
object_layers[-1].wmaps[name]= weights
def read_morph(morph_bytes, object_layers):
'''Read an endomorph's displacement values.'''
def read_morph(morph_bytes, object_layers, is_abs):
'''Read an endomorph's relative or absolute displacement values.'''
chunk_len= len(morph_bytes)
offset= 2
name, name_len= read_lwostring(morph_bytes[offset:])
offset+= name_len
deltas= []
while offset < chunk_len:
pnt_id, pnt_id_len= read_vx(morph_bytes[offset:offset+4])
offset+= pnt_id_len
pos= struct.unpack(">fff", morph_bytes[offset:offset+12])
pos= struct.unpack(">fff", morph_bytes[offset:offset+12])
offset+= 12
deltas.append([pnt_id, pos[0], pos[1], pos[2]])
object_layers[-1].morphs[name]= deltas
pnt= object_layers[-1].pnts[pnt_id]
if is_abs:
deltas.append([pnt_id, pos[0], pos[2], pos[1]])
else:
# Swap the Y and Z to match Blender's pitch.
deltas.append([pnt_id, pnt[0]+pos[0], pnt[1]+pos[2], pnt[2]+pos[1]])
object_layers[-1].morphs[name]= deltas
def read_colmap(col_bytes, object_layers):
'''Read the RGB or RGBA color map.'''
......@@ -445,29 +468,29 @@ def read_colmap(col_bytes, object_layers):
name, name_len= read_lwostring(col_bytes[offset:])
offset+= name_len
colors= {}
if dia == 3:
while offset < chunk_len:
pnt_id, pnt_id_len= read_vx(col_bytes[offset:offset+4])
offset+= pnt_id_len
col= struct.unpack(">fff", col_bytes[offset:offset+12])
col= struct.unpack(">fff", col_bytes[offset:offset+12])
offset+= 12
colors[pnt_id]= (col[0], col[1], col[2])
elif dia == 4:
while offset < chunk_len:
pnt_id, pnt_id_len= read_vx(col_bytes[offset:offset+4])
offset+= pnt_id_len
col= struct.unpack(">ffff", col_bytes[offset:offset+16])
col= struct.unpack(">ffff", col_bytes[offset:offset+16])
offset+= 16
colors[pnt_id]= (col[0], col[1], col[2])
if name in object_layers[-1].colmaps:
if "PointMap" in object_layers[-1].colmaps[name]:
object_layers[-1].colmaps[name]["PointMap"].update(colors)
else:
object_layers[-1].colmaps[name]["PointMap"]= colors
else:
object_layers[-1].colmaps[name]= dict(PointMap= colors)
object_layers[-1].colmaps[name]= dict(PointMap=colors)
def read_color_vmad(col_bytes, object_layers, last_pols_count):
......@@ -479,17 +502,17 @@ def read_color_vmad(col_bytes, object_layers, last_pols_count):
offset+= name_len
colors= {}
abs_pid= len(object_layers[-1].pols) - last_pols_count
if dia == 3:
while offset < chunk_len:
pnt_id, pnt_id_len= read_vx(col_bytes[offset:offset+4])
offset+= pnt_id_len
pol_id, pol_id_len= read_vx(col_bytes[offset:offset+4])
offset+= pol_id_len
# The PolyID in a VMAD can be relative, this offsets it.
pol_id+= abs_pid
col= struct.unpack(">fff", col_bytes[offset:offset+12])
col= struct.unpack(">fff", col_bytes[offset:offset+12])
offset+= 12
if pol_id in colors:
colors[pol_id][pnt_id]= (col[0], col[1], col[2])
......@@ -501,23 +524,23 @@ def read_color_vmad(col_bytes, object_layers, last_pols_count):
offset+= pnt_id_len
pol_id, pol_id_len= read_vx(col_bytes[offset:offset+4])
offset+= pol_id_len
pol_id+= abs_pid
col= struct.unpack(">ffff", col_bytes[offset:offset+16])
col= struct.unpack(">ffff", col_bytes[offset:offset+16])
offset+= 16
if pol_id in colors:
colors[pol_id][pnt_id]= (col[0], col[1], col[2])
else:
colors[pol_id]= dict({pnt_id: (col[0], col[1], col[2])})
if name in object_layers[-1].colmaps:
if "FaceMap" in object_layers[-1].colmaps[name]:
object_layers[-1].colmaps[name]["FaceMap"].update(colors)
else:
object_layers[-1].colmaps[name]["FaceMap"]= colors
else:
object_layers[-1].colmaps[name]= dict(FaceMap= colors)
object_layers[-1].colmaps[name]= dict(FaceMap=colors)
def read_uvmap(uv_bytes, object_layers):
'''Read the simple UV coord values.'''
......@@ -526,21 +549,21 @@ def read_uvmap(uv_bytes, object_layers):
name, name_len= read_lwostring(uv_bytes[offset:])
offset+= name_len
uv_coords= {}
while offset < chunk_len:
pnt_id, pnt_id_len= read_vx(uv_bytes[offset:offset+4])
offset+= pnt_id_len
pos= struct.unpack(">ff", uv_bytes[offset:offset+8])
pos= struct.unpack(">ff", uv_bytes[offset:offset+8])
offset+= 8
uv_coords[pnt_id]= (pos[0], pos[1])
if name in object_layers[-1].uvmaps:
if "PointMap" in object_layers[-1].uvmaps[name]:
object_layers[-1].uvmaps[name]["PointMap"].update(uv_coords)
else:
object_layers[-1].uvmaps[name]["PointMap"]= uv_coords
else:
object_layers[-1].uvmaps[name]= dict(PointMap= uv_coords)
object_layers[-1].uvmaps[name]= dict(PointMap=uv_coords)
def read_uv_vmad(uv_bytes, object_layers, last_pols_count):
......@@ -551,28 +574,69 @@ def read_uv_vmad(uv_bytes, object_layers, last_pols_count):
offset+= name_len
uv_coords= {}
abs_pid= len(object_layers[-1].pols) - last_pols_count
while offset < chunk_len:
pnt_id, pnt_id_len= read_vx(uv_bytes[offset:offset+4])
offset+= pnt_id_len
pol_id, pol_id_len= read_vx(uv_bytes[offset:offset+4])
offset+= pol_id_len
pol_id+= abs_pid
pos= struct.unpack(">ff", uv_bytes[offset:offset+8])
pos= struct.unpack(">ff", uv_bytes[offset:offset+8])
offset+= 8
if pol_id in uv_coords:
uv_coords[pol_id][pnt_id]= (pos[0], pos[1])
else:
uv_coords[pol_id]= dict({pnt_id: (pos[0], pos[1])})
if name in object_layers[-1].uvmaps:
if "FaceMap" in object_layers[-1].uvmaps[name]:
object_layers[-1].uvmaps[name]["FaceMap"].update(uv_coords)
else:
object_layers[-1].uvmaps[name]["FaceMap"]= uv_coords
else:
object_layers[-1].uvmaps[name]= dict(FaceMap= uv_coords)
object_layers[-1].uvmaps[name]= dict(FaceMap=uv_coords)
def read_weight_vmad(ew_bytes, object_layers):
'''Read the VMAD Weight values.'''
chunk_len= len(ew_bytes)
offset= 2
name, name_len= read_lwostring(ew_bytes[offset:])
if name != "Edge Weight":
return # We just want the Catmull-Clark edge weights
offset+= name_len
prev_pol= -1
prev_pnt= -1
prev_weight= 0.0
first_pnt= -1
poly_pnts= 0
while offset < chunk_len:
pnt_id, pnt_id_len= read_vx(ew_bytes[offset:offset+4])
offset+= pnt_id_len
pol_id, pol_id_len= read_vx(ew_bytes[offset:offset+4])
offset+= pol_id_len
weight,= struct.unpack(">f", ew_bytes[offset:offset+4])
offset+= 4
if prev_pol == pol_id:
# Points on the same poly should define an edge.
object_layers[-1].edge_weights["{0} {1}".format(prev_pnt, pnt_id)]= weight
poly_pnts += 1
else:
if poly_pnts > 2:
# Make an edge from the first and last points.
object_layers[-1].edge_weights["{0} {1}".format(first_pnt, prev_pnt)]= prev_weight
first_pnt= pnt_id
prev_pol= pol_id
poly_pnts= 1
prev_pnt= pnt_id
prev_weight= weight
if poly_pnts > 2:
object_layers[-1].edge_weights["{0} {1}".format(first_pnt, prev_pnt)]= prev_weight
def read_pols(pol_bytes, object_layers):
......@@ -581,7 +645,7 @@ def read_pols(pol_bytes, object_layers):
offset= 0
pols_count = len(pol_bytes)
old_pols_count= len(object_layers[-1].pols)
while offset < pols_count:
pnts_count,= struct.unpack(">H", pol_bytes[offset:offset+2])
offset+= 2
......@@ -590,9 +654,9 @@ def read_pols(pol_bytes, object_layers):
face_pnt, data_size= read_vx(pol_bytes[offset:offset+4])
offset+= data_size
all_face_pnts.append(face_pnt)
object_layers[-1].pols.append(all_face_pnts)
return len(object_layers[-1].pols) - old_pols_count
......@@ -606,7 +670,7 @@ def read_pols_5(pol_bytes, object_layers):
chunk_len= len(pol_bytes)
old_pols_count= len(object_layers[-1].pols)
poly= 0
while offset < chunk_len:
pnts_count,= struct.unpack(">H", pol_bytes[offset:offset+2])
offset+= 2
......@@ -615,7 +679,7 @@ def read_pols_5(pol_bytes, object_layers):
face_pnt,= struct.unpack(">H", pol_bytes[offset:offset+2])
offset+= 2
all_face_pnts.append(face_pnt)
object_layers[-1].pols.append(all_face_pnts)
sid,= struct.unpack(">h", pol_bytes[offset:offset+2])
offset+= 2
......@@ -624,7 +688,7 @@ def read_pols_5(pol_bytes, object_layers):
object_layers[-1].surf_tags[sid]= []
object_layers[-1].surf_tags[sid].append(poly)
poly+= 1
return len(object_layers[-1].pols) - old_pols_count
......@@ -633,7 +697,7 @@ def read_bones(bone_bytes, object_layers):
print("\tReading Layer ("+object_layers[-1].name+") Bones")
offset= 0
bones_count = len(bone_bytes)
while offset < bones_count:
pnts_count,= struct.unpack(">H", bone_bytes[offset:offset+2])
offset+= 2
......@@ -642,36 +706,36 @@ def read_bones(bone_bytes, object_layers):
bone_pnt, data_size= read_vx(bone_bytes[offset:offset+4])
offset+= data_size
all_bone_pnts.append(bone_pnt)
object_layers[-1].bones.append(all_bone_pnts)
def read_bone_tags(tag_bytes, object_layers, object_tags, type):
'''Read the bone name or roll tags.'''
offset= 0
chunk_len= len(tag_bytes)
if type == 'BONE':
bone_dict= object_layers[-1].bone_names
elif type == 'BNUP':
bone_dict= object_layers[-1].bone_rolls
else:
return
while offset < chunk_len:
pid, pid_len= read_vx(tag_bytes[offset:offset+4])
offset+= pid_len
tid,= struct.unpack(">H", tag_bytes[offset:offset+2])
offset+= 2
bone_dict[pid]= object_tags[tid]
def read_surf_tags(tag_bytes, object_layers, last_pols_count):
'''Read the list of PolyIDs and tag indexes.'''
print("\tReading Layer ("+object_layers[-1].name+") Surface Assignments")
offset= 0
chunk_len= len(tag_bytes)
# Read in the PolyID/Surface Index pairs.
abs_pid= len(object_layers[-1].pols) - last_pols_count
while offset < chunk_len:
......@@ -682,18 +746,18 @@ def read_surf_tags(tag_bytes, object_layers, last_pols_count):
if sid not in object_layers[-1].surf_tags:
object_layers[-1].surf_tags[sid]= []
object_layers[-1].surf_tags[sid].append(pid + abs_pid)
def read_surf(surf_bytes, object_surfs):
'''Read the object's surface data.'''
if len(object_surfs) == 0:
print("Reading Object Surfaces")
surf= _obj_surf()
name, name_len= read_lwostring(surf_bytes)
if len(name) != 0:
surf.name = name
# We have to read this, but we won't use it...yet.
s_name, s_name_len= read_lwostring(surf_bytes[name_len:])
offset= name_len+s_name_len
......@@ -703,65 +767,65 @@ def read_surf(surf_bytes, object_surfs):
offset+= 4
subchunk_len,= struct.unpack(">H", surf_bytes[offset:offset+2])
offset+= 2
# Now test which subchunk it is.
if subchunk_name == b'COLR':
surf.colr= struct.unpack(">fff", surf_bytes[offset:offset+12])
# Don't bother with any envelopes for now.
elif subchunk_name == b'DIFF':
surf.diff,= struct.unpack(">f", surf_bytes[offset:offset+4])
elif subchunk_name == b'LUMI':
surf.lumi,= struct.unpack(">f", surf_bytes[offset:offset+4])
elif subchunk_name == b'SPEC':
surf.spec,= struct.unpack(">f", surf_bytes[offset:offset+4])
elif subchunk_name == b'REFL':
surf.refl,= struct.unpack(">f", surf_bytes[offset:offset+4])
elif subchunk_name == b'RBLR':
surf.rblr,= struct.unpack(">f", surf_bytes[offset:offset+4])
elif subchunk_name == b'TRAN':
surf.tran,= struct.unpack(">f", surf_bytes[offset:offset+4])
elif subchunk_name == b'RIND':
surf.rind,= struct.unpack(">f", surf_bytes[offset:offset+4])
elif subchunk_name == b'TBLR':
surf.tblr,= struct.unpack(">f", surf_bytes[offset:offset+4])
elif subchunk_name == b'TRNL':
surf.trnl,= struct.unpack(">f", surf_bytes[offset:offset+4])
elif subchunk_name == b'GLOS':
surf.glos,= struct.unpack(">f", surf_bytes[offset:offset+4])
elif subchunk_name == b'SHRP':
surf.shrp,= struct.unpack(">f", surf_bytes[offset:offset+4])
elif subchunk_name == b'SMAN':
s_angle,= struct.unpack(">f", surf_bytes[offset:offset+4])
if s_angle > 0.0:
surf.smooth = True
offset+= subchunk_len
object_surfs[surf.name]= surf
def read_surf_5(surf_bytes, object_surfs):
'''Read the object's surface data.'''
if len(object_surfs) == 0:
print("Reading Object Surfaces")
surf= _obj_surf()
name, name_len= read_lwostring(surf_bytes)
if len(name) != 0:
surf.name = name
offset= name_len
chunk_len= len(surf_bytes)
while offset < chunk_len:
......@@ -769,12 +833,12 @@ def read_surf_5(surf_bytes, object_surfs):
offset+= 4
subchunk_len,= struct.unpack(">H", surf_bytes[offset:offset+2])
offset+= 2
# Now test which subchunk it is.
if subchunk_name == b'COLR':
color= struct.unpack(">BBBB", surf_bytes[offset:offset+4])
surf.colr= [color[0] / 255.0, color[1] / 255.0, color[2] / 255.0]
elif subchunk_name == b'DIFF':
surf.diff,= struct.unpack(">h", surf_bytes[offset:offset+2])
surf.diff/= 256.0 # Yes, 256 not 255.
......@@ -782,11 +846,11 @@ def read_surf_5(surf_bytes, object_surfs):
elif subchunk_name == b'LUMI':
surf.lumi,= struct.unpack(">h", surf_bytes[offset:offset+2])
surf.lumi/= 256.0
elif subchunk_name == b'SPEC':
surf.spec,= struct.unpack(">h", surf_bytes[offset:offset+2])
surf.spec/= 256.0
elif subchunk_name == b'REFL':
surf.refl,= struct.unpack(">h", surf_bytes[offset:offset+2])
surf.refl/= 256.0
......@@ -794,27 +858,27 @@ def read_surf_5(surf_bytes, object_surfs):
elif subchunk_name == b'TRAN':
surf.tran,= struct.unpack(">h", surf_bytes[offset:offset+2])
surf.tran/= 256.0
elif subchunk_name == b'RIND':
surf.rind,= struct.unpack(">f", surf_bytes[offset:offset+4])
elif subchunk_name == b'GLOS':
surf.glos,= struct.unpack(">h", surf_bytes[offset:offset+2])
elif subchunk_name == b'SMAN':
s_angle,= struct.unpack(">f", surf_bytes[offset:offset+4])
if s_angle > 0.0:
surf.smooth = True
offset+= subchunk_len
object_surfs[surf.name]= surf
def create_mappack(data, map_name, map_type):
'''Match the map data to faces.'''
pack= {}
def color_pointmap(map):
for fi in range(len(data.pols)):
if fi not in pack:
......@@ -824,7 +888,7 @@ def create_mappack(data, map_name, map_type):
pack[fi].append(map[pnt])
else:
pack[fi].append((1.0, 1.0, 1.0))
def color_facemap(map):
for fi in range(len(data.pols)):
if fi not in pack:
......@@ -836,7 +900,7 @@ def create_mappack(data, map_name, map_type):
if data.pols[fi][po] in map[fi]:
pack[fi].insert(po, map[fi][data.pols[fi][po]])
del pack[fi][po+1]
def uv_pointmap(map):
for fi in range(len(data.pols)):
if fi not in pack:
......@@ -848,7 +912,7 @@ def create_mappack(data, map_name, map_type):
if pnt_id in map:
pack[fi].insert(po, map[pnt_id])
del pack[fi][po+1]
def uv_facemap(map):
for fi in range(len(data.pols)):
if fi not in pack:
......@@ -861,31 +925,31 @@ def create_mappack(data, map_name, map_type):
if pnt_id in map[fi]:
pack[fi].insert(po, map[fi][pnt_id])
del pack[fi][po+1]
if map_type == "COLOR":
# Look at the first map, is it a point or face map
if "PointMap" in data.colmaps[map_name]:
color_pointmap(data.colmaps[map_name]["PointMap"])
if "FaceMap" in data.colmaps[map_name]:
color_facemap(data.colmaps[map_name]["FaceMap"])
elif map_type == "UV":
if "PointMap" in data.uvmaps[map_name]:
uv_pointmap(data.uvmaps[map_name]["PointMap"])
if "FaceMap" in data.uvmaps[map_name]:
uv_facemap(data.uvmaps[map_name]["FaceMap"])
return pack
def build_armature(layer_data, bones):
'''Build an armature from the skelegon data in the mesh.'''
print("Building Armature")
# New Armatures include a default bone, remove it.
bones.remove(bones[0])
# Now start adding the bones at the point locations.
prev_bone= None
for skb_idx in range(len(layer_data.bones)):
......@@ -893,10 +957,10 @@ def build_armature(layer_data, bones):
nb= bones.new(layer_data.bone_names[skb_idx])
else:
nb= bones.new("Bone")
nb.head= layer_data.pnts[layer_data.bones[skb_idx][0]]
nb.tail= layer_data.pnts[layer_data.bones[skb_idx][1]]
if skb_idx in layer_data.bone_rolls:
xyz= layer_data.bone_rolls[skb_idx].split(' ')
vec= mathutils.Vector()
......@@ -908,18 +972,18 @@ def build_armature(layer_data, bones):
# XXX: This code may need a second look and test.
else:
nb.roll= 0.0
if prev_bone != None:
if nb.head == prev_bone.tail:
nb.parent= prev_bone
nb.use_connect= True
prev_bone= nb
def build_objects(object_layers, object_surfs, object_tags, object_name, add_subd_mod, skel_to_arm):
'''Using the gathered data, create the objects.'''
ob_dict= {} # Used for the parenting setup.
ob_dict= {} # Used for the parenting setup.
print("Adding %d Materials" % len(object_surfs))
for surf_key in object_surfs:
......@@ -940,21 +1004,20 @@ def build_objects(object_layers, object_surfs, object_tags, object_name, add_sub
surf_data.bl_mat.raytrace_transparency.ior= surf_data.rind
surf_data.bl_mat.raytrace_transparency.gloss_factor= 1.0 - surf_data.tblr
surf_data.bl_mat.translucency= surf_data.trnl
surf_data.bl_mat.specular_hardness= int(4*((10*surf_data.glos)*(10*surf_data.glos)))
# XXX: The gloss converion needs another look
surf_data.bl_mat.specular_hardness= int(4*((10*surf_data.glos)*(10*surf_data.glos)))+4
# The Gloss is as close as possible given the differences.
# Single layer objects use the object file's name instead.
if len(object_layers) and object_layers[-1].name == 'Layer 1':
object_layers[-1].name= object_name
print("Building '%s' Object" % object_name)
else:
print("Building %d Objects" % len(object_layers))
for layer_data in object_layers:
me= bpy.data.meshes.new(layer_data.name)
me= bpy.data.meshes.new(layer_data.name)
me.vertices.add(len(layer_data.pnts))
me.faces.add(len(layer_data.pols))
# for vi in range(len(layer_data.pnts)):
# me.vertices[vi].co= layer_data.pnts[vi]
......@@ -962,47 +1025,42 @@ def build_objects(object_layers, object_surfs, object_tags, object_name, add_sub
me.vertices.foreach_set("co", [axis co for co in layer_data.pnts for axis in co])
ngons= {} # To keep the FaceIdx consistant, handle NGons later.
has_edges= False
edges= [] # Holds the FaceIdx of the 2-point polys.
for fi, fpol in enumerate(len(layer_data.pols)):
fpol.reverse() # Reversing gives correct normal directions
# PointID 0 in the last element causes Blender to think it's un-used.
if fpol[-1] == 0:
fpol.insert(0, fpol[-1])
del fpol[-1]
vlen= len(fpol)
if vlen == 3 or vlen == 4:
for i in range(vlen):
me.faces[fi].vertices_raw[i]= fpol[i]
elif vlen == 2:
has_edges= True
# This IS an odd way to create edges, but using edges.add() was causing
# crashes if there were faces and edges being created in the same layer.
# XXX, this should set edges instead!
opp= fpol
me.faces[fi].vertices= opp[0], opp[1], opp[0]
edges.append(fi)
elif vlen != 1:
ngons[fi]= fpol # Deal with them later
ob= bpy.data.objects.new(layer_data.name, me)
bpy.context.scene.objects.link(ob)
ob_dict[layer_data.index]= [ob, layer_data.parent_index]
# Move the object so the pivot is in the right place.
ob.location= layer_data.pivot
# Create the Material Slots and assign the MatIndex to the correct faces.
mat_slot= 0
for surf_key in layer_data.surf_tags:
if object_tags[surf_key] in object_surfs:
me.materials.append(object_surfs[object_tags[surf_key]].bl_mat)
for fi in layer_data.surf_tags[surf_key]:
me.faces[fi].material_index= mat_slot
me.faces[fi].use_smooth= object_surfs[object_tags[surf_key]].smooth
mat_slot+=1
# Create the Vertex Groups (LW's Weight Maps).
if len(layer_data.wmaps) > 0:
print("Adding %d Vertex Groups" % len(layer_data.wmaps))
......@@ -1012,7 +1070,7 @@ def build_objects(object_layers, object_surfs, object_tags, object_name, add_sub
wlist= layer_data.wmaps[wmap_key]
for pvp in wlist:
ob.vertex_groups.assign([pvp[0]], vgroup, pvp[1], 'REPLACE')
# Create the Shape Keys (LW's Endomorphs).
if len(layer_data.morphs) > 0:
print("Adding %d Shapes Keys" % len(layer_data.morphs))
......@@ -1021,8 +1079,8 @@ def build_objects(object_layers, object_surfs, object_tags, object_name, add_sub
skey= ob.add_shape_key(morph_key)
dlist= layer_data.morphs[morph_key]
for pdp in dlist:
me.shape_keys.keys[skey.name].data[pdp[0]].co= [layer_data.pnts[pdp[0]][0]+pdp[1], layer_data.pnts[pdp[0]][1]+pdp[3], layer_data.pnts[pdp[0]][2]+pdp[2]]
me.shape_keys.keys[skey.name].data[pdp[0]].co= [pdp[1], pdp[2], pdp[3]]
# Create the Vertex Color maps.
if len(layer_data.colmaps) > 0:
print("Adding %d Vertex Color Maps" % len(layer_data.colmaps))
......@@ -1036,14 +1094,14 @@ def build_objects(object_layers, object_surfs, object_tags, object_name, add_sub
continue
face= map_pack[fi]
colf= vcol.data[fi]
if len(face) > 2:
colf.color1= face[0]
colf.color2= face[1]
colf.color3= face[2]
if len(face) == 4:
colf.color4= face[3]
# Create the UV Maps.
if len(layer_data.uvmaps) > 0:
print("Adding %d UV Textures" % len(layer_data.uvmaps))
......@@ -1057,7 +1115,7 @@ def build_objects(object_layers, object_surfs, object_tags, object_name, add_sub
continue
face= map_pack[fi]
uvf= uvm.data[fi]
if len(face) > 2:
uvf.uv1= face[0]
uvf.uv2= face[1]
......@@ -1083,13 +1141,33 @@ def build_objects(object_layers, object_surfs, object_tags, object_name, add_sub
face.material_index= me.faces[ng_key].material_index
face.use_smooth= me.faces[ng_key].use_smooth
face_offset+= 1
# FaceIDs are no longer a concern, so now update the mesh.
has_edges= len(edges) > 0 or len(layer_data.edge_weights) > 0
me.update(calc_edges=has_edges)
# Add the edges.
edge_offset= len(me.edges)
me.edges.add(len(edges))
for edge_fi in edges:
me.edges[edge_offset].vertices[0]= layer_data.pols[edge_fi][0]
me.edges[edge_offset].vertices[1]= layer_data.pols[edge_fi][1]
edge_offset+= 1
# Apply the Edge Weighting.
if len(layer_data.edge_weights) > 0:
for edge in me.edges:
edge_sa= "{0} {1}".format(edge.vertices[0]), edge.vertices[1])
edge_sb= "{0} {1}".format(edge.vertices[1]), edge.vertices[0])
if edge_sa in layer_data.edge_weights:
edge.crease= layer_data.edge_weights[edge_sa]
elif edge_sb in layer_data.edge_weights:
edge.crease= layer_data.edge_weights[edge_sb]
# Unfortunately we can't exlude certain faces from the subdivision.
if layer_data.has_subds and add_subd_mod:
ob.modifiers.new(name="Subsurf", type='SUBSURF')
# Should we build an armature from the embedded rig?
if len(layer_data.bones) > 0 and skel_to_arm:
bpy.ops.object.armature_add()
......@@ -1100,7 +1178,7 @@ def build_objects(object_layers, object_surfs, object_tags, object_name, add_sub
bpy.ops.object.mode_set(mode='EDIT')
build_armature(layer_data, arm_object.data.edit_bones)
bpy.ops.object.mode_set(mode='OBJECT')
# Clear out the dictionaries for this layer.
layer_data.bone_names.clear()
layer_data.bone_rolls.clear()
......@@ -1109,34 +1187,35 @@ def build_objects(object_layers, object_surfs, object_tags, object_name, add_sub
layer_data.uvmaps.clear()
layer_data.morphs.clear()
layer_data.surf_tags.clear()
# With the objects made, setup the parents and re-adjust the locations.
for ob_key in ob_dict:
if ob_dict[ob_key][1] != -1 and ob_dict[ob_key][1] in ob_dict:
parent_ob = ob_dict[ob_dict[ob_key][1]]
ob_dict[ob_key][0].parent= parent_ob[0]
ob_dict[ob_key][0].location-= parent_ob[0].location
bpy.context.scene.update()
print("Done Importing LWO File")
from bpy.props import *
class IMPORT_OT_lwo(bpy.types.Operator):
'''Import LWO Operator.'''
bl_idname= "import_scene.lwo"
bl_label= "Import LWO"
bl_description= "Import a LightWave Object file."
bl_options= {'REGISTER', 'UNDO'}
filepath= StringProperty(name="File Path", description="Filepath used for importing the LWO file", maxlen=1024, default="")
ADD_SUBD_MOD= BoolProperty(name="Apply SubD Modifier", description="Apply the Subdivision Surface modifier to layers with Subpatches", default= True)
LOAD_HIDDEN= BoolProperty(name="Load Hidden Layers", description="Load object layers that have been marked as hidden", default= False)
SKEL_TO_ARM= BoolProperty(name="Create Armature", description="Create an armature from an embedded Skelegon rig", default= True)
ADD_SUBD_MOD= BoolProperty(name="Apply SubD Modifier", description="Apply the Subdivision Surface modifier to layers with Subpatches", default=True)
LOAD_HIDDEN= BoolProperty(name="Load Hidden Layers", description="Load object layers that have been marked as hidden", default=False)
SKEL_TO_ARM= BoolProperty(name="Create Armature", description="Create an armature from an embedded Skelegon rig", default=True)
def execute(self, context):
load_lwo(self.properties.filepath,
context,
......@@ -1144,7 +1223,7 @@ class IMPORT_OT_lwo(bpy.types.Operator):
self.properties.LOAD_HIDDEN,
self.properties.SKEL_TO_ARM)
return {'FINISHED'}
def invoke(self, context, event):
wm= context.window_manager
wm.add_fileselect(self)
......@@ -1156,7 +1235,7 @@ def menu_func(self, context):
def register():
bpy.types.INFO_MT_file_import.append(menu_func)
def unregister():
bpy.types.INFO_MT_file_import.remove(menu_func)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment