PDA

View Full Version : Doom3 Game Meshes script



clintonman
03-12-2012, 11:19 PM
This script is a WIP, but I can import some meshes. The head shows the lostsoul mesh which is the simplest one I could find in the game. All the surfaces were recreated by hand using textures from the game. The script creates the surfaces and names them but that's all it does. The man shown in the second image causes the script to error out because of not enough empty layers or something like that.
My first goal is to fix the errors, add error catching to the code and use better variable names. After that is skeleton import in Layout and then animation import. The final goal is to have full skeletal mesh and animation export.


#! /usr/bin/env python
# -*- Mode: Python -*-
# -*- coding: ascii -*-

"""
This is a LightWave Command Sequence plug-in (Modeler) that
loads an md5mesh file
"""

import sys
import math
import lwsdk

__author__ = "Clinton Reese"
__date__ = "Mar 7 2012"
__copyright__ = "Copyright (C) 2012 Clinton's 3D Creations"
__version__ = "1.0"
__maintainer__ = "Clinton Reese"
__email__ = "[email protected]"
__status__ = "Example modified 3"
__lwver__ = "11"

#position
class xyz(object):
def __init__(self,x,y,z):
self.x = x
self.y = y
self.z = z

#quaternion rotation
class xyzw(object):
def __init__(self,x,y,z):
self.x = x
self.y = y
self.z = z
#calculate quaternion w term
wq = 1.0 - x*x - y*y - z*z
if wq < 0.0:
self.w = 0.0
else:
self.w = -math.sqrt(wq)

class joint(object):
def __init__(self,dataline):
self.name = dataline[0].strip('"')
self.parentindex = int(dataline[1])
self.position = xyz(float(dataline[3]),float(dataline[4]),float(dataline[5]))
self.rotation = xyzw(float(dataline[8]),float(dataline[9]),float(dataline[10]))

#vertex data - uv values, weight index and number of consecutive weights influencing the vertex
class vert(object):
def __init__(self,dataline):
self.u = float(dataline[3])
self.v = float(dataline[4])
self.weightIndex = int(dataline[6])
self.numWeights = int(dataline[7])

#mesh triangles vertex indices
class tri(object):
def __init__(self,dataline):
self.i = int(dataline[2])
self.j = int(dataline[3])
self.k = int(dataline[4])

#vertex weight value from the joint and xyz position relative to the joint position
class weight(object):
def __init__(self,dataline):
self.jointindex = int(dataline[2])
self.weight = float(dataline[3])
self.x = float(dataline[5])
self.y = float(dataline[6])
self.z = float(dataline[7])

class import_md5mesh(lwsdk.ICommandSequence):
def __init__(self, context):
super(import_md5mesh, self).__init__()
self._filepath = ""

def createSkelegons(self, joints, mod_command):
edit_op_result = lwsdk.EDERR_NONE
# /LightWave11.0/sdk/lwsdk11.0/html/classes/me.html
# /LightWave11.0/sdk/lwsdk11.0/include/lwmeshedt.h
mesh_edit_op = mod_command.editBegin(0, 0, lwsdk.OPSEL_USER)

#TODO: fix fat mesh error
#TODO: new lwo on load
#TODO: better ui?
#TODO: any way to auto install the script?
#TODO: document locations in html docs
#TODO: **** error checking *****
if not mesh_edit_op:
print >>sys.stderr, 'Failed to engage mesh edit operations!'
# return lwsdk.AFUNC_OK
allptID = [] #LWPntID
for j in joints:
pt = [j.position.x, j.position.y, j.position.z]
allptID.append(mesh_edit_op.addPoint(mesh_edit_op. state, pt))
if j.parentindex != -1:
skelptID = []
skelptID.append(allptID[j.parentindex])
skelptID.append(allptID[-1])
#note: looks like skelegon names go into Parts
#problem is joints != bones so don't develope skelegons
# or use fixed size floating bones and use orientation data
# no need a meaningful heirarchy
polygon = mesh_edit_op.addPoly(mesh_edit_op.state, lwsdk.LWPOLTYPE_BONE, None, "Default", skelptID)
#didn't work - got parts but skelegons still called "Bone 0"
mesh_edit_op.polTag(mesh_edit_op.state, polygon, lwsdk.LWPTAG_PART, j.name+"SUB")
print 'joint: ',j.name

mesh_edit_op.done(mesh_edit_op.state, edit_op_result, 0)

def createMesh(self, shader, meshname, joints, verts, tris, weights, mod_command):
edit_op_result = lwsdk.EDERR_NONE
allptID = []
mesh_edit_op = mod_command.editBegin(0, 0, lwsdk.OPSEL_USER)
for j in verts:
weightCount = j.numWeights
posX = 0.0
posY = 0.0
posZ = 0.0
while weightCount > 0:
weightCount -= 1
#get weight index and position values
wtX = weights[j.weightIndex + weightCount].x
wtY = weights[j.weightIndex + weightCount].y
wtZ = weights[j.weightIndex + weightCount].z
jointIndex = weights[j.weightIndex + weightCount].jointindex
weightValue = weights[j.weightIndex + weightCount].weight
#get joint rotations
qx = joints[jointIndex].rotation.x
qy = joints[jointIndex].rotation.y
qz = joints[jointIndex].rotation.z
qw = joints[jointIndex].rotation.w
#quaternion rotation matrix
rm11=1.0 - 2.0*qy*qy - 2.0*qz*qz
rm12=2.0*qx*qy - 2.0*qw*qz
rm13=2.0*qx*qz+2.0*qw*qy
rm21=2.0*qx*qy+2.0*qw*qz
rm22=1.0 - 2.0*qx*qx - 2.0*qz*qz
rm23=2.0*qy*qz - 2.0*qw*qx
rm31=2.0*qx*qz - 2.0*qw*qy
rm32=2.0*qy*qz+2.0*qw*qx
rm33=1.0 - 2.0*qx*qx - 2.0*qy*qy
#rotate point position relative to joint
#rposX = rm11*wtX + rm21*wtY + rm31*wtZ
#rposY = rm12*wtX + rm22*wtY + rm32*wtZ
#rposZ = rm13*wtX + rm23*wtY + rm33*wtZ
rposX = rm11*wtX + rm12*wtY + rm13*wtZ
rposY = rm21*wtX + rm22*wtY + rm23*wtZ
rposZ = rm31*wtX + rm32*wtY + rm33*wtZ # inverse rotation

#final position is the weighted sum of the vertex relative to each joint that influences it
posX = posX +(joints[jointIndex].position.x + rposX)*weightValue;
posY = posY +(joints[jointIndex].position.y + rposY)*weightValue;
posZ = posZ +(joints[jointIndex].position.z + rposZ)*weightValue;

pt = [posX, posY, posZ]
allptID.append(mesh_edit_op.addPoint(mesh_edit_op. state, pt))

#set uv texture map values, note that the v coordinate is reversed
#the final result looks creased down the center so maybe more adjustment needed?
uv = []
uv.append(j.u)
uv.append(1.0-j.v)
mesh_edit_op.pntVMap(mesh_edit_op.state, allptID[-1], lwsdk.LWVMAP_TXUV, meshname+"_UV", uv)

#create weight maps for each joint
weightCount = j.numWeights
while weightCount > 0:
weightCount -= 1
jointIndex = weights[j.weightIndex + weightCount].jointindex
weightValue = weights[j.weightIndex + weightCount].weight
jointName = joints[jointIndex].name
mesh_edit_op.pntVMap(mesh_edit_op.state, allptID[-1], lwsdk.LWVMAP_WGHT, jointName+"_WT", weightValue)

# create the triangle mesh faces
for t in tris:
polyptID = []
polyptID.append(allptID[t.k])
polyptID.append(allptID[t.j])
polyptID.append(allptID[t.i])
polygon = mesh_edit_op.addPoly(mesh_edit_op.state, lwsdk.LWPOLTYPE_FACE, None, shader, polyptID)
mesh_edit_op.done(mesh_edit_op.state, edit_op_result, 0)

def get_commands(self, mod_command):
command_list = {} #LWCommandCode
# /LightWave11.0/sdk/lwsdk11.0/html/commands/modeler.html
for command in ["SETLAYER",
"SETLAYERNAME"]:
command_list[command] = mod_command.lookup(mod_command.data, command)
return command_list

# LWCommandSequence -----------------------------------
def process(self, mod_command):
# panels
# /LightWave11.0/sdk/lwsdk11.0/html/globals/panel.html
# /LightWave11.0/sdk/lwsdk11.0/include/lwpanel.h
ui = lwsdk.LWPanels()
panel = ui.create('MD5Mesh Import')

controlWidth = 128
c1 = panel.load_ctl('Select File',controlWidth)
#c1 = panel.loadbutton_ctl('LoadIt',controlWidth)
c1.set_str(self._filepath)

if panel.open(lwsdk.PANF_BLOCKING | lwsdk.PANF_CANCEL) == 0:
ui.destroy(panel)
return lwsdk.AFUNC_OK

self._filepath = c1.get_str()

print self._filepath

fileObject = open(self._filepath)

fileline = fileObject.readline()

if fileline != 'MD5Version 10\n':
print >>sys.stderr, 'Not a valid MD5 version 10 file!'
print fileline
return lwsdk.AFUNC_OK

cs_dict = self.get_commands(mod_command)

# limited to 4 layers??
empty_layers = lwsdk.LWStateQueryFuncs().layerList(lwsdk.OPLYR_EM PTY, None)
empty_layers_list = empty_layers.split()

joints = []
jointmode = False
meshmode = False
meshbegin = False
meshname = ''
shader = ''
layerindex = 0
numverts = 0
numtris = 0
numweights = 0
verts = []
tris = []
weights = []

for fileline in fileObject:
if fileline == '\n': continue
dataline = fileline.split()

if dataline[0] == 'numJoints':
numJoints = dataline[1]
print 'numJoints = ', numJoints
if dataline[0] == "numMeshes":
numMeshes = dataline[1]
print 'numMeshes = ', numMeshes

if fileline.startswith('}'):
if jointmode:
self.createSkelegons(joints, mod_command)

if meshmode:
self.createMesh(shader, meshname, joints, verts, tris, weights, mod_command)
print 'Exit mesh mode'

jointmode = False
meshmode = False

if jointmode:
joints.append(joint(dataline))

if meshmode:
if meshbegin:
meshbegin = False

if dataline[1] == 'meshes:':
meshname = dataline[2]
layerindex += 1
cs_options = lwsdk.marshall_dynavalues(empty_layers_list[layerindex])
result = mod_command.execute(mod_command.data, cs_dict["SETLAYER"], cs_options, lwsdk.OPSEL_USER)
cs_options = lwsdk.marshall_dynavalues(meshname)
result = mod_command.execute(mod_command.data, cs_dict["SETLAYERNAME"], cs_options, lwsdk.OPSEL_USER)

if dataline[0] == 'shader':
shader = dataline[1].strip('"')
print shader

if dataline[0] == 'numverts':
numverts = dataline[1]
print 'numverts ',numverts
if dataline[0] == 'vert':
verts.append(vert(dataline))

if dataline[0] == 'numtris':
numtris = dataline[1]
print 'numtris ',numtris
if dataline[0] == 'tri':
tris.append(tri(dataline))

if dataline[0] == 'numweights':
numweights = dataline[1]
print 'numweights ',numweights
if dataline[0] == 'weight':
weights.append(weight(dataline))

#read joint data
if fileline.startswith('joint'):
jointmode = True
cs_options = lwsdk.marshall_dynavalues(empty_layers_list[layerindex])
result = mod_command.execute(mod_command.data, cs_dict["SETLAYER"], cs_options, lwsdk.OPSEL_USER)
cs_options = lwsdk.marshall_dynavalues('theSkelegons')
result = mod_command.execute(mod_command.data, cs_dict["SETLAYERNAME"], cs_options, lwsdk.OPSEL_USER)
print 'enter joint mode'
#read mesh data
if fileline.startswith('mesh'):
meshmode = True
meshbegin= True
verts = []
tris = []
weights = []
print 'enter mesh mode'

fileObject.close()
print 'import complete'

return lwsdk.AFUNC_OK

# /LightWave11.0/sdk/lwsdk11.0/html/server.html
ServerTagInfo = [
( "Python ImportMD5Mesh", lwsdk.SRVTAG_USERNAME | lwsdk.LANGID_USENGLISH ),
( "Import MD5Mesh", lwsdk.SRVTAG_BUTTONNAME | lwsdk.LANGID_USENGLISH ),
( "Utilities/Python", lwsdk.SRVTAG_MENU | lwsdk.LANGID_USENGLISH )
]

ServerRecord = { lwsdk.CommandSequenceFactory("LW_PyImportMD5Mesh", import_md5mesh) : ServerTagInfo }

papou
03-13-2012, 06:12 PM
Ooo, i tought mesh in Doom3 were lwo...

clintonman
03-13-2012, 07:20 PM
Ooo, i tought mesh in Doom3 were lwo...
Actually, you're right. Doom3 reads ASE and LWO files as static meshes and uses md5mesh and md5anim files for skeletal meshes.

clintonman
03-21-2012, 06:44 PM
Here's the code(in a raw state) for importing the bones. It uses some of the sdk quaternion code translated to python to handle the rotation conversions. There is a bug in the SDK that prevents parent in place from working, so I'll shelve this project until it gets fixed. New image shows the mouth opened by the jaw bone.


#! /usr/bin/env python
# -*- Mode: Python -*-
# -*- coding: ascii -*-

"""
This is a LightWave Command Sequence plug-in (Modeler) that
loads an md5mesh skeleton
"""

import os
import sys
import math
#import numarray
#import NumPy
#import Numeric
import lwsdk

__author__ = "Clinton Reese"
__date__ = "Mar 15 2012"
__copyright__ = "Copyright (C) 2012 Clinton's 3D Creations"
__version__ = "1.0"
__maintainer__ = "Clinton Reese"
__email__ = "[email protected]"
__status__ = "Example modified 3"
__lwver__ = "11"

#position
class xyz(object):
def __init__(self,x,y,z):
self.x = x
self.y = y
self.z = z

#quaternion rotation
class xyzw(object):
def __init__(self,x,y,z):
self.x = x
self.y = y
self.z = z
#calculate quaternion w term
wq = 1.0 - x*x - y*y - z*z
if wq < 0.0:
self.w = 0.0
else:
self.w = -math.sqrt(wq)

class joint(object):
def __init__(self,dataline):
self.name = dataline[0].strip('"')
self.parentindex = int(dataline[1])
self.itemID = 0
self.position = xyz(float(dataline[3]),float(dataline[4]),float(dataline[5]))
self.rotation = xyzw(float(dataline[8]),float(dataline[9]),float(dataline[10]))


class import_md5skeleton(lwsdk.IGeneric):
def __init__(self, context):
super(import_md5skeleton, self).__init__()
self._filepath = ""

#from sdk eulerangle and quattype
def Eul_FromHMatrix(self, M, order):
#EulerAngles ea;
ea = xyz(0.0, 0.0, 0.0)
#EulGetOrd unpacks all useful information about order simultaneously.
#EulGetOrd(ord,i,j,k,h,n,s,f) {unsigned o=ord;f=o&1;o>>=1;s=o&1;o>>=1;\
#n=o&1;o>>=1;i=EulSafe[o&3];j=EulNext[i+n];k=EulNext[i+1-n];h=s?k:i;}
#define EulSafe "\000\001\002\000"
#define EulNext "\001\002\000\001"
EulSafe = [0,1,2,0]
EulNext = [1,2,0,1]
o = order
f=o&1
o>>=1
s=o&1
o>>=1
n=o&1
o>>=1
i=EulSafe[o&3]
j=EulNext[i+n]
k=EulNext[i+1-n]
#false is zero
#h=s?k:i
if s!=0: h=k
else: h=i

#int i,j,k,h,n,s,f;
#EulGetOrd(order,i,j,k,h,n,s,f);
EulRepYes = 1
FLT_EPSILON = 1.e-5
if s==EulRepYes:
sy = math.sqrt(M[i+j*4]*M[i+j*4] + M[i+k*4]*M[i+k*4])
if sy > 16*FLT_EPSILON:
ea.x = math.atan2(M[i+j*4], M[i+k*4])
ea.y = math.atan2(sy, M[i+i*4])
ea.z = math.atan2(M[j+i*4], -M[k+i*4])
else:
ea.x = math.atan2(-M[j+k*4], M[j+j*4])
ea.y = math.atan2(sy, M[i+i*4])
ea.z = 0
else:
cy = math.sqrt(M[i+i*4]*M[i+i*4] + M[j+i*4]*M[j+i*4])
if cy > 16*FLT_EPSILON:
ea.x = math.atan2(M[k+j*4], M[k+k*4])
ea.y = math.atan2(-M[k+i*4], cy)
ea.z = math.atan2(M[j+i*4], M[i+i*4])
else:
ea.x = math.atan2(-M[j+k*4], M[j+j*4])
ea.y = math.atan2(-M[k+i*4], cy)
ea.z = 0

EulParOdd = 1
EulFrmR = 1

#if (n==EulParOdd) {ea.x = -ea.x; ea.y = - ea.y; ea.z = -ea.z;}
if n==EulParOdd:
ea.x = -ea.x
ea.y = - ea.y
ea.z = -ea.z;
#if (f==EulFrmR) {double t = ea.x; ea.x = ea.z; ea.z = t;}
if f==EulFrmR:
t = ea.x
ea.x = ea.z
ea.z = t

#ea.w = order;
ea.w = o
return ea


#from sdk eulerangle and quattype
#Convert quaternion to Euler angles (in radians)
def Eul_FromQuat(self, q, order):
#HMatrix M;
#M = zeros((4,4))
M = []
for i in range(16):
M.append(0)
Nq = q.x*q.x+q.y*q.y+q.z*q.z+q.w*q.w
#double s = (Nq > 0.0) ? (2.0 / Nq) : 0.0;
if Nq > 0.0:
s = 2.0 / Nq
else:
s = 0.0
xs = q.x*s
ys = q.y*s
zs = q.z*s
wx = q.w*xs
wy = q.w*ys
wz = q.w*zs
xx = q.x*xs
xy = q.x*ys
xz = q.x*zs
yy = q.y*ys
yz = q.y*zs
zz = q.z*zs
#M[X][X] = 1.0 - (yy + zz); M[X][Y] = xy - wz; M[X][Z] = xz + wy;
M[0+0*4] = 1.0 - (yy + zz)
M[0+1*4] = xy - wz
M[0+2*4] = xz + wy
#M[Y][X] = xy + wz; M[Y][Y] = 1.0 - (xx + zz); M[Y][Z] = yz - wx;
M[1+0*4] = xy + wz
M[1+1*4] = 1.0 - (xx + zz)
M[1+2*4] = yz - wx
#M[Z][X] = xz - wy; M[Z][Y] = yz + wx; M[Z][Z] = 1.0 - (xx + yy);
M[2+0*4] = xz - wy
M[2+1*4] = yz + wx
M[2+2*4] = 1.0 - (xx + yy)
#M[W][X]=M[W][Y]=M[W][Z]=M[X][W]=M[Y][W]=M[Z][W]=0.0; M[W][W]=1.0;
M[3+0*4]=M[3+1*4]=M[3+2*4]=M[0+3*4]=M[1+3*4]=M[2+3*4]=0.0
M[3+3*4]=1.0

quatrot = self.Eul_FromHMatrix(M, order)
#return (Eul_FromHMatrix(M, order));
return quatrot

# LWCommandSequence -----------------------------------
def process(self, ga):
# panels
# /LightWave11.0/sdk/lwsdk11.0/html/globals/panel.html
# /LightWave11.0/sdk/lwsdk11.0/include/lwpanel.h
ui = lwsdk.LWPanels()
panel = ui.create('MD5Skeleton Import')

controlWidth = 128
c1 = panel.load_ctl('Select File',controlWidth)
#c1 = panel.loadbutton_ctl('LoadIt',controlWidth)
c1.set_str(self._filepath)

if panel.open(lwsdk.PANF_BLOCKING | lwsdk.PANF_CANCEL) == 0:
ui.destroy(panel)
return lwsdk.AFUNC_OK

self._filepath = c1.get_str()

print self._filepath

fileObject = open(self._filepath)

fileline = fileObject.readline()

if fileline != 'MD5Version 10\n':
print >>sys.stderr, 'Not a valid MD5 version 10 file!'
print fileline
return lwsdk.AFUNC_OK

interface_info = lwsdk.LWInterfaceInfo()
selected_items = interface_info.selected_items()

# /LightWave11.0/sdk/lwsdk11.0/html/globals/iteminfo.html
# /LightWave11.0/sdk/lwsdk11.0/include/lwrender.h
#LWItemInfo *iteminfo;
#iteminfo = global( LWITEMINFO_GLOBAL, GFUSE_TRANSIENT );

#if mesh not selected create a null to add the bones
if not selected_items:
result = ga.evaluate(ga.data, "AddNull buildNULL")
interface_info = lwsdk.LWInterfaceInfo()
selected_items = interface_info.selected_items()
buildItemID = selected_items[0]
#print 'test: nothing selected'
else:
item_info = lwsdk.LWItemInfo()
mytype = item_info.type(selected_items[0])
#print 'something selected'
#print mytype
if mytype==lwsdk.LWI_OBJECT:
buildItemID = selected_items[0]
#print 'mesh selected'
else:
result = ga.evaluate(ga.data, "AddNull buildNULL")
interface_info = lwsdk.LWInterfaceInfo()
selected_items = interface_info.selected_items()
buildItemID = selected_items[0]
#print 'selected is not a mesh'

#result = ga.evaluate(ga.data, "Position 0 1 0")

# /LightWave11.0/sdk/lwsdk11.0/html/globals/sceneobj.html
#LWObjectFuncs *objfunc;
#objfunc = global( LWOBJECTFUNCS_GLOBAL, GFUSE_TRANSIENT );

#get weightmap info for the scene
object_functions = lwsdk.LWObjectFuncs()
numWeights = object_functions.numVMaps(lwsdk.LWVMAP_WGHT)
print 'weight count ', numWeights

joints = []
jointmode = False
RADtoDEG = 180.0/math.pi

#need to read state before running the toggle
#result = ga.evaluate(ga.data, "ParentInPlace")

for fileline in fileObject:
if fileline == '\n': continue
dataline = fileline.split()

if dataline[0] == 'numJoints':
numJoints = dataline[1]
print 'numJoints = ', numJoints

if fileline.startswith('}'):
if jointmode:
firstJoint = True
for j in joints:
ga.evaluate(ga.data, "SelectItem %s" % lwsdk.itemid_to_str(buildItemID))
layoutCommand = "AddBone " + j.name
result = ga.evaluate(ga.data, layoutCommand)
selected_items = interface_info.selected_items()
j.itemID = selected_items[0]

parentIndex = j.parentindex

#global position from md5mesh file
layoutCommand = "Position " + str(j.position.x) + " " + str(j.position.y) + " " + str(j.position.z)
#print layoutCommand
result = ga.evaluate(ga.data, layoutCommand)

if firstJoint:
result = ga.evaluate(ga.data, "BoneRestLength 3")

else:
lenX = joints[parentIndex].position.x - j.position.x
lenY = joints[parentIndex].position.y - j.position.y
lenZ = joints[parentIndex].position.z - j.position.z
boneLength = math.sqrt(lenX*lenX + lenY*lenY + lenZ*lenZ)*0.6 #dont use full bone length
result = ga.evaluate(ga.data, "BoneRestLength "+str(boneLength))


#X = 0
#EulParOdd = 1
#EulRepNo = 0
#EulFrmR = 1
#EulOrd(i,p,r,f) (((((((i)<<1)+(p))<<1)+(r))<<1)+(f))
#binary 0101 = 5 decimal
#EulOrdYXZr = EulOrd(X,EulParOdd,EulRepNo,EulFrmR)
EulOrdYXZr = 5
genericEul = self.Eul_FromQuat(j.rotation, EulOrdYXZr)

#rotprint = "Euler Rotation " + str(genericEul.x) + " " + str(genericEul.y) + " " + str(genericEul.z)
#print rotprint
rotx = RADtoDEG*genericEul.x
roty = RADtoDEG*genericEul.y
rotz = RADtoDEG*genericEul.z
layoutCommand = "Rotation " + str(rotx) + " " + str(roty) + " " + str(rotz)
#print layoutCommand
result = ga.evaluate(ga.data, layoutCommand)

# if 'autokey' is not turned on, we need to explicitly
# create keys for the object at the current time offset
interface_info = lwsdk.LWInterfaceInfo()
if not (interface_info.generalFlags & lwsdk.LWGENF_AUTOKEY):
ga.evaluate(ga.data, "CreateKey %f" % interface_info.curTime)

if firstJoint:
firstJoint = False
else:
#sprintf( cmd, "ParentItem %x",itemID[parentIndex[curJoint]]);//hex number
#layoutCommand = "ParentItem " + joints[parentIndex].itemID
#result = ga.evaluate(ga.data, layoutCommand)
print parentIndex
#ga.evaluate(ga.data, "ParentItem %s" % lwsdk.itemid_to_str(joints[parentIndex].itemID))

#set weight after transform and parenting
# TODO may need test and adjustment for bone with no influence
if(numWeights > 0):
for i in range(numWeights):
#weightName = objfunc->vmapName( LWVMAP_WGHT, curwt )
weightName = object_functions.vmapName(lwsdk.LWVMAP_WGHT, i)
weightNameInLW = j.name + '_WT'
if weightName == weightNameInLW:
layoutCommand = 'BoneWeightMapName ' + weightNameInLW
result = ga.evaluate(ga.data, layoutCommand)
result = ga.evaluate(ga.data, 'BoneWeightMapOnly')

jointmode = False

if jointmode:
joints.append(joint(dataline))


#read joint data
if fileline.startswith('joint'):
jointmode = True
print 'enter joint mode'

fileObject.close()
print 'import complete'

return lwsdk.AFUNC_OK

# /LightWave11.0/sdk/lwsdk11.0/html/server.html
ServerTagInfo = [
( "Python ImportMD5Skeleton", lwsdk.SRVTAG_USERNAME | lwsdk.LANGID_USENGLISH ),
( "Import MD5Skeleton", lwsdk.SRVTAG_BUTTONNAME | lwsdk.LANGID_USENGLISH ),
( "Utilities/Python", lwsdk.SRVTAG_MENU | lwsdk.LANGID_USENGLISH )
]

ServerRecord = { lwsdk.GenericFactory("LW_PyImportMD5Skeleton", import_md5skeleton) : ServerTagInfo }

Gorbag
04-10-2012, 11:07 AM
Nice work, Clinton. :thumbsup:

clintonman
04-10-2012, 06:04 PM
I just realized I didn't update this thread. My parent in place question was answered in another thread so I didn't "shelve" this project. All the parts are basically working and I have import and export of the skeletal mesh and animations working. It just needs some more work and testing to call it "done".

clintonman
04-23-2012, 03:25 PM
First version is done. The scripts can be found at the bottom of the page here:
http://www.clintons3d.com/plugins/lightwave/index.html

There are 5 scripts for loading the mesh to modeler, loading the skeleton, animation and exporting the skeletal mesh and animations.
It just needs a proper writeup for how to use them.