forked from bartvdbraak/blender
Scripts:
Misc updates to the ac3d importer and exporter: - use Mesh instead of NMesh; - properly export modified data and materials from either ob or obdata (thanks for mesh.getFromObject :) ); - option to export local rot and loc info; - better import / export of hierarchies; - + tiny updates here and there to support old or weird .ac files.
This commit is contained in:
parent
d90c686bdb
commit
82bfd281f2
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
""" Registration info for Blender menus:
|
""" Registration info for Blender menus:
|
||||||
Name: 'AC3D (.ac)...'
|
Name: 'AC3D (.ac)...'
|
||||||
Blender: 236
|
Blender: 242
|
||||||
Group: 'Export'
|
Group: 'Export'
|
||||||
Tip: 'Export selected meshes to AC3D (.ac) format'
|
Tip: 'Export selected meshes to AC3D (.ac) format'
|
||||||
"""
|
"""
|
||||||
@ -10,7 +10,7 @@ Tip: 'Export selected meshes to AC3D (.ac) format'
|
|||||||
__author__ = "Willian P. Germano"
|
__author__ = "Willian P. Germano"
|
||||||
__url__ = ("blender", "elysiun", "AC3D's homepage, http://www.ac3d.org",
|
__url__ = ("blender", "elysiun", "AC3D's homepage, http://www.ac3d.org",
|
||||||
"PLib 3d gaming lib, http://plib.sf.net")
|
"PLib 3d gaming lib, http://plib.sf.net")
|
||||||
__version__ = "2.41a 2006-06-16"
|
__version__ = "2.43 2007-01-14"
|
||||||
|
|
||||||
__bpydoc__ = """\
|
__bpydoc__ = """\
|
||||||
This script exports selected Blender meshes to AC3D's .ac file format.
|
This script exports selected Blender meshes to AC3D's .ac file format.
|
||||||
@ -37,6 +37,7 @@ Config Options:<br>
|
|||||||
toggle:<br>
|
toggle:<br>
|
||||||
- AC3D 4 mode: unset it to export without the 'crease' tag that was
|
- AC3D 4 mode: unset it to export without the 'crease' tag that was
|
||||||
introduced with AC3D 4.0 and with the old material handling;<br>
|
introduced with AC3D 4.0 and with the old material handling;<br>
|
||||||
|
- global coords: transform all vertices of all meshes to global coordinates;<br>
|
||||||
- skip data: set it if you don't want mesh names (ME:, not OB: field)
|
- skip data: set it if you don't want mesh names (ME:, not OB: field)
|
||||||
to be exported as strings for AC's "data" tags (19 chars max);<br>
|
to be exported as strings for AC's "data" tags (19 chars max);<br>
|
||||||
- rgb mirror color can be exported as ambient and/or emissive if needed,
|
- rgb mirror color can be exported as ambient and/or emissive if needed,
|
||||||
@ -56,10 +57,13 @@ to export (read notes below about tokens, too);<br>
|
|||||||
toggle is "on".
|
toggle is "on".
|
||||||
|
|
||||||
Notes:<br>
|
Notes:<br>
|
||||||
This version is considerably faster than previous ones for large meshes;<br>
|
This version updates:<br>
|
||||||
|
- modified meshes are correctly exported, no need to apply the modifiers in Blender;<br>
|
||||||
|
- correctly export each used material, be it assigned to the object or to its mesh data;<br>
|
||||||
|
- exporting lines (edges) is again supported;<br>
|
||||||
|
- there's a new option to choose between exporting meshes with transformed (global) coordinates or local ones;<br>
|
||||||
Multiple textures per mesh are supported (mesh gets split);<br>
|
Multiple textures per mesh are supported (mesh gets split);<br>
|
||||||
Parenting with meshes or empties as parents is converted to AC3D group
|
Parents are exported as a group containing both the parent and its children;<br>
|
||||||
information;<br>
|
|
||||||
Start mesh object names (OB: field) with "!" or "#" if you don't want them to be exported;<br>
|
Start mesh object names (OB: field) with "!" or "#" if you don't want them to be exported;<br>
|
||||||
Start mesh object names (OB: field) with "=" or "$" to prevent them from being split (meshes with multiple textures or both textured and non textured faces are split unless this trick is used or the "no split" option is set.
|
Start mesh object names (OB: field) with "=" or "$" to prevent them from being split (meshes with multiple textures or both textured and non textured faces are split unless this trick is used or the "no split" option is set.
|
||||||
"""
|
"""
|
||||||
@ -67,17 +71,20 @@ information;<br>
|
|||||||
# $Id$
|
# $Id$
|
||||||
#
|
#
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# AC3DExport version 2.41
|
# AC3DExport version 2.43
|
||||||
# Program versions: Blender 2.36+ and AC3Db files (means version 0xb)
|
# Program versions: Blender 2.42+ and AC3Db files (means version 0xb)
|
||||||
# new: faster, supports multiple textures per object and parenting is
|
# new: updated for new Blender version and Mesh module; supports lines (edges) again;
|
||||||
# properly exported as group info, adapted to work with the Config Editor
|
# option to export vertices transformed to global coordinates or not; now the modified
|
||||||
|
# (by existing mesh modifiers) mesh is exported; materials are properly exported, no
|
||||||
|
# matter if each of them is linked to the mesh or to the object.
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# Thanks: Steve Baker for discussions and inspiration; for testing, bug
|
# Thanks: Steve Baker for discussions and inspiration; for testing, bug
|
||||||
# reports, suggestions: David Megginson, Filippo di Natale, Franz Melchior
|
# reports, suggestions, patches: David Megginson, Filippo di Natale,
|
||||||
|
# Franz Melchior, Campbell Barton, Josh Babcock, Ralf Gerlich
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# ***** BEGIN GPL LICENSE BLOCK *****
|
# ***** BEGIN GPL LICENSE BLOCK *****
|
||||||
#
|
#
|
||||||
# Copyright (C) 2004: Willian P. Germano, wgermano _at_ ig.com.br
|
# Copyright (C) 2004-2007: Willian P. Germano, wgermano _at_ ig.com.br
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@ -94,8 +101,8 @@ information;<br>
|
|||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
|
|
||||||
import Blender
|
import Blender
|
||||||
|
from Blender import Object, Mesh, Material, Image, Mathutils, Registry
|
||||||
from Blender import sys as bsys
|
from Blender import sys as bsys
|
||||||
from Blender import Mathutils
|
|
||||||
|
|
||||||
# Globals
|
# Globals
|
||||||
ERROR_MSG = '' # popup error msg
|
ERROR_MSG = '' # popup error msg
|
||||||
@ -108,11 +115,18 @@ REPORT_DATA = {
|
|||||||
}
|
}
|
||||||
TOKENS_DONT_EXPORT = ['!', '#']
|
TOKENS_DONT_EXPORT = ['!', '#']
|
||||||
TOKENS_DONT_SPLIT = ['=', '$']
|
TOKENS_DONT_SPLIT = ['=', '$']
|
||||||
MATIDX_ERROR = False
|
|
||||||
|
MATIDX_ERROR = 0
|
||||||
|
|
||||||
|
# flags:
|
||||||
|
LOOSE = Mesh.EdgeFlags['LOOSE']
|
||||||
|
FACE_TWOSIDED = Mesh.FaceModes['TWOSIDE']
|
||||||
|
MESH_TWOSIDED = Mesh.Modes['TWOSIDED']
|
||||||
|
|
||||||
REG_KEY = 'ac3d_export'
|
REG_KEY = 'ac3d_export'
|
||||||
|
|
||||||
# config options:
|
# config options:
|
||||||
|
GLOBAL_COORDS = True
|
||||||
SKIP_DATA = False
|
SKIP_DATA = False
|
||||||
MIRCOL_AS_AMB = False
|
MIRCOL_AS_AMB = False
|
||||||
MIRCOL_AS_EMIS = False
|
MIRCOL_AS_EMIS = False
|
||||||
@ -126,6 +140,7 @@ EXPORT_DIR = ''
|
|||||||
PER_FACE_1_OR_2_SIDED = True
|
PER_FACE_1_OR_2_SIDED = True
|
||||||
|
|
||||||
tooltips = {
|
tooltips = {
|
||||||
|
'GLOBAL_COORDS': "transform all vertices of all meshes to global coordinates",
|
||||||
'SKIP_DATA': "don't export mesh names as data fields",
|
'SKIP_DATA': "don't export mesh names as data fields",
|
||||||
'MIRCOL_AS_AMB': "export mirror color as ambient color",
|
'MIRCOL_AS_AMB': "export mirror color as ambient color",
|
||||||
'MIRCOL_AS_EMIS': "export mirror color as emissive color",
|
'MIRCOL_AS_EMIS': "export mirror color as emissive color",
|
||||||
@ -153,10 +168,11 @@ def update_RegistryInfo():
|
|||||||
d['ONLY_SELECTED'] = ONLY_SELECTED
|
d['ONLY_SELECTED'] = ONLY_SELECTED
|
||||||
d['PER_FACE_1_OR_2_SIDED'] = PER_FACE_1_OR_2_SIDED
|
d['PER_FACE_1_OR_2_SIDED'] = PER_FACE_1_OR_2_SIDED
|
||||||
d['tooltips'] = tooltips
|
d['tooltips'] = tooltips
|
||||||
Blender.Registry.SetKey(REG_KEY, d, True)
|
d['GLOBAL_COORDS'] = GLOBAL_COORDS
|
||||||
|
Registry.SetKey(REG_KEY, d, True)
|
||||||
|
|
||||||
# Looking for a saved key in Blender.Registry dict:
|
# Looking for a saved key in Blender.Registry dict:
|
||||||
rd = Blender.Registry.GetKey(REG_KEY, True)
|
rd = Registry.GetKey(REG_KEY, True)
|
||||||
|
|
||||||
if rd:
|
if rd:
|
||||||
try:
|
try:
|
||||||
@ -171,6 +187,7 @@ if rd:
|
|||||||
ONLY_SELECTED = rd['ONLY_SELECTED']
|
ONLY_SELECTED = rd['ONLY_SELECTED']
|
||||||
NO_SPLIT = rd['NO_SPLIT']
|
NO_SPLIT = rd['NO_SPLIT']
|
||||||
PER_FACE_1_OR_2_SIDED = rd['PER_FACE_1_OR_2_SIDED']
|
PER_FACE_1_OR_2_SIDED = rd['PER_FACE_1_OR_2_SIDED']
|
||||||
|
GLOBAL_COORDS = rd['GLOBAL_COORDS']
|
||||||
except KeyError: update_RegistryInfo()
|
except KeyError: update_RegistryInfo()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -180,7 +197,7 @@ VERBOSE = True
|
|||||||
CONFIRM_OVERWRITE = True
|
CONFIRM_OVERWRITE = True
|
||||||
|
|
||||||
# check General scripts config key for default behaviors
|
# check General scripts config key for default behaviors
|
||||||
rd = Blender.Registry.GetKey('General', True)
|
rd = Registry.GetKey('General', True)
|
||||||
if rd:
|
if rd:
|
||||||
try:
|
try:
|
||||||
VERBOSE = rd['verbose']
|
VERBOSE = rd['verbose']
|
||||||
@ -194,9 +211,10 @@ DEFAULT_MAT = \
|
|||||||
spec 0.5 0.5 0.5 shi 64 trans 0'
|
spec 0.5 0.5 0.5 shi 64 trans 0'
|
||||||
|
|
||||||
# This transformation aligns Blender and AC3D coordinate systems:
|
# This transformation aligns Blender and AC3D coordinate systems:
|
||||||
acmatrix = Mathutils.Matrix([1,0,0,0], [0,0,-1,0], [0,1,0,0], [0,0,0,1])
|
BLEND_TO_AC3D_MATRIX = Mathutils.Matrix([1,0,0,0], [0,0,-1,0], [0,1,0,0], [0,0,0,1])
|
||||||
|
|
||||||
def Round(f):
|
def Round_s(f):
|
||||||
|
"Round to default precision and turn value to a string"
|
||||||
r = round(f,6) # precision set to 10e-06
|
r = round(f,6) # precision set to 10e-06
|
||||||
if r == int(r):
|
if r == int(r):
|
||||||
return str(int(r))
|
return str(int(r))
|
||||||
@ -206,11 +224,15 @@ def Round(f):
|
|||||||
def transform_verts(verts, m):
|
def transform_verts(verts, m):
|
||||||
vecs = []
|
vecs = []
|
||||||
for v in verts:
|
for v in verts:
|
||||||
vec = Mathutils.Vector([v[0],v[1],v[2], 1])
|
x, y, z = v.co
|
||||||
#vecs.append(Mathutils.VecMultMat(vec, m))
|
vec = Mathutils.Vector([x, y, z, 1])
|
||||||
vecs.append(vec*m)
|
vecs.append(vec*m)
|
||||||
return vecs
|
return vecs
|
||||||
|
|
||||||
|
def get_loose_edges(mesh):
|
||||||
|
loose = LOOSE
|
||||||
|
return [e for e in mesh.edges if e.flag & loose]
|
||||||
|
|
||||||
# ---
|
# ---
|
||||||
|
|
||||||
# meshes with more than one texture assigned
|
# meshes with more than one texture assigned
|
||||||
@ -242,29 +264,27 @@ class FooMesh:
|
|||||||
def __init__(self, tex, faces, mesh):
|
def __init__(self, tex, faces, mesh):
|
||||||
self.name = mesh.name
|
self.name = mesh.name
|
||||||
self.mesh = mesh
|
self.mesh = mesh
|
||||||
self.faces = []
|
self.looseEdges = []
|
||||||
self.verts = verts = []
|
self.faceUV = mesh.faceUV
|
||||||
|
self.degr = mesh.degr
|
||||||
vidxs = [0]*len(mesh.verts)
|
vidxs = [0]*len(mesh.verts)
|
||||||
faces2 = [0]*len(faces)
|
foofaces = []
|
||||||
for f in faces:
|
for f in faces:
|
||||||
self.faces.append(self.FooFace(self, f))
|
foofaces.append(self.FooFace(self, f))
|
||||||
for v in f.v:
|
for v in f.v:
|
||||||
if v: vidxs[v.index] = 1
|
if v: vidxs[v.index] = 1
|
||||||
i = 0
|
i = 0
|
||||||
|
fooverts = []
|
||||||
for v in mesh.verts:
|
for v in mesh.verts:
|
||||||
if vidxs[v.index]:
|
if vidxs[v.index]:
|
||||||
verts.append(v)
|
fooverts.append(v)
|
||||||
vidxs[v.index] = i
|
vidxs[v.index] = i
|
||||||
i += 1
|
i += 1
|
||||||
for f in self.faces:
|
for f in foofaces:
|
||||||
for v in f.v:
|
for v in f.v:
|
||||||
if v: v.index = vidxs[v.v.index]
|
if v: v.index = vidxs[v.v.index]
|
||||||
|
self.faces = foofaces
|
||||||
def hasFaceUV(self):
|
self.verts = fooverts
|
||||||
return self.mesh.hasFaceUV()
|
|
||||||
|
|
||||||
def getMaxSmoothAngle(self):
|
|
||||||
return self.mesh.getMaxSmoothAngle()
|
|
||||||
|
|
||||||
|
|
||||||
class AC3DExport: # the ac3d exporter part
|
class AC3DExport: # the ac3d exporter part
|
||||||
@ -272,15 +292,14 @@ class AC3DExport: # the ac3d exporter part
|
|||||||
def __init__(self, scene_objects, filename):
|
def __init__(self, scene_objects, filename):
|
||||||
|
|
||||||
global ARG, SKIP_DATA, ADD_DEFAULT_MAT, DEFAULT_MAT
|
global ARG, SKIP_DATA, ADD_DEFAULT_MAT, DEFAULT_MAT
|
||||||
global ERROR_MSG, MATIDX_ERROR
|
global ERROR_MSG
|
||||||
|
|
||||||
MATIDX_ERROR = 0
|
|
||||||
|
|
||||||
header = 'AC3Db'
|
header = 'AC3Db'
|
||||||
self.buf = ''
|
self.buf = ''
|
||||||
self.mbuf = ''
|
self.mbuf = ''
|
||||||
self.mlist = []
|
self.mlist = []
|
||||||
world_kids = 0
|
world_kids = 0
|
||||||
|
parents_list = self.parents_list = []
|
||||||
kids_dict = self.kids_dict = {}
|
kids_dict = self.kids_dict = {}
|
||||||
objs = []
|
objs = []
|
||||||
exp_objs = self.exp_objs = []
|
exp_objs = self.exp_objs = []
|
||||||
@ -299,37 +318,50 @@ class AC3DExport: # the ac3d exporter part
|
|||||||
objs = \
|
objs = \
|
||||||
[o for o in scene_objects if o.type in ['Mesh', 'Empty']]
|
[o for o in scene_objects if o.type in ['Mesh', 'Empty']]
|
||||||
|
|
||||||
|
# create a tree from parents to children objects
|
||||||
|
|
||||||
for obj in objs[:]:
|
for obj in objs[:]:
|
||||||
parent = obj.parent
|
parent = obj.parent
|
||||||
list = [obj]
|
lineage = [obj]
|
||||||
|
|
||||||
while parent:
|
while parent:
|
||||||
|
parents_list.append(parent.name)
|
||||||
obj = parent
|
obj = parent
|
||||||
parent = parent.getParent()
|
parent = parent.getParent()
|
||||||
list.insert(0, obj)
|
lineage.insert(0, obj)
|
||||||
|
|
||||||
dict = tree
|
d = tree
|
||||||
for i in xrange(len(list)):
|
for i in xrange(len(lineage)):
|
||||||
lname = list[i].getType()[:2] + list[i].name
|
lname = lineage[i].getType()[:2] + lineage[i].name
|
||||||
if lname not in dict.keys():
|
if lname not in d.keys():
|
||||||
dict[lname] = {}
|
d[lname] = {}
|
||||||
dict = dict[lname]
|
d = d[lname]
|
||||||
|
|
||||||
|
# traverse the tree to get an ordered list of names of objects to export
|
||||||
self.traverse_dict(tree)
|
self.traverse_dict(tree)
|
||||||
|
|
||||||
world_kids = len(tree.keys())
|
world_kids = len(tree.keys())
|
||||||
|
|
||||||
objlist = [Blender.Object.Get(name) for name in exp_objs]
|
# get list of objects to export, start writing the .ac file
|
||||||
|
|
||||||
|
objlist = [Object.Get(name) for name in exp_objs]
|
||||||
|
|
||||||
meshlist = [o for o in objlist if o.type == 'Mesh']
|
meshlist = [o for o in objlist if o.type == 'Mesh']
|
||||||
|
|
||||||
self.MATERIALS(meshlist)
|
# create a temporary mesh to hold actual (modified) mesh data
|
||||||
|
TMP_mesh = Mesh.New('tmp_for_ac_export')
|
||||||
|
|
||||||
|
# write materials
|
||||||
|
|
||||||
|
self.MATERIALS(meshlist, TMP_mesh)
|
||||||
if not self.mbuf or ADD_DEFAULT_MAT:
|
if not self.mbuf or ADD_DEFAULT_MAT:
|
||||||
self.mbuf = DEFAULT_MAT + '\n' + self.mbuf
|
self.mbuf = "%s\n%s" % (DEFAULT_MAT, self.mbuf)
|
||||||
file.write(self.mbuf)
|
file.write(self.mbuf)
|
||||||
|
|
||||||
file.write('OBJECT world\nkids %s\n' % world_kids)
|
file.write('OBJECT world\nkids %s\n' % world_kids)
|
||||||
|
|
||||||
|
# write the objects
|
||||||
|
|
||||||
for obj in objlist:
|
for obj in objlist:
|
||||||
self.obj = obj
|
self.obj = obj
|
||||||
|
|
||||||
@ -337,21 +369,33 @@ class AC3DExport: # the ac3d exporter part
|
|||||||
objname = obj.name
|
objname = obj.name
|
||||||
kidsnum = kids_dict[objname]
|
kidsnum = kids_dict[objname]
|
||||||
|
|
||||||
|
# A parent plus its children are exported as a group.
|
||||||
|
# If the parent is a mesh, its rot and loc are exported as the
|
||||||
|
# group rot and loc and the mesh (w/o rot and loc) is added to the group.
|
||||||
if kidsnum:
|
if kidsnum:
|
||||||
self.OBJECT('group')
|
self.OBJECT('group')
|
||||||
parent_is_mesh = 0
|
self.name(objname)
|
||||||
if objtype == 'Mesh':
|
if objtype == 'Mesh':
|
||||||
kidsnum += 1
|
kidsnum += 1
|
||||||
parent_is_mesh = 1
|
if not GLOBAL_COORDS:
|
||||||
self.name(objname)
|
localmatrix = obj.getMatrix('localspace')
|
||||||
|
if not obj.getParent():
|
||||||
|
localmatrix *= BLEND_TO_AC3D_MATRIX
|
||||||
|
self.rot(localmatrix.rotationPart())
|
||||||
|
self.loc(localmatrix.translationPart())
|
||||||
self.kids(kidsnum)
|
self.kids(kidsnum)
|
||||||
|
|
||||||
if objtype == 'Mesh':
|
if objtype == 'Mesh':
|
||||||
mesh = self.mesh = obj.getData()
|
mesh = TMP_mesh # temporary mesh to hold actual (modified) mesh data
|
||||||
meshes = self.split_mesh(mesh)
|
mesh.getFromObject(objname)
|
||||||
|
self.mesh = mesh
|
||||||
|
if mesh.faceUV:
|
||||||
|
meshes = self.split_mesh(mesh)
|
||||||
|
else:
|
||||||
|
meshes = [mesh]
|
||||||
if len(meshes) > 1:
|
if len(meshes) > 1:
|
||||||
if NO_SPLIT or self.dont_split(objname):
|
if NO_SPLIT or self.dont_split(objname):
|
||||||
self.export_mesh(mesh, obj)
|
self.export_mesh(mesh, ob)
|
||||||
REPORT_DATA['nosplit'].append(objname)
|
REPORT_DATA['nosplit'].append(objname)
|
||||||
else:
|
else:
|
||||||
self.OBJECT('group')
|
self.OBJECT('group')
|
||||||
@ -370,27 +414,27 @@ class AC3DExport: # the ac3d exporter part
|
|||||||
file.close()
|
file.close()
|
||||||
REPORT_DATA['main'].append("Done. Saved to: %s" % filename)
|
REPORT_DATA['main'].append("Done. Saved to: %s" % filename)
|
||||||
|
|
||||||
def traverse_dict(self, dict):
|
def traverse_dict(self, d):
|
||||||
kids_dict = self.kids_dict
|
kids_dict = self.kids_dict
|
||||||
exp_objs = self.exp_objs
|
exp_objs = self.exp_objs
|
||||||
keys = dict.keys()
|
keys = d.keys()
|
||||||
for k in keys:
|
for k in keys:
|
||||||
objname = k[2:]
|
objname = k[2:]
|
||||||
klen = len(dict[k])
|
klen = len(d[k])
|
||||||
kids_dict[objname] = klen
|
kids_dict[objname] = klen
|
||||||
if self.dont_export(objname):
|
if self.dont_export(objname):
|
||||||
dict.pop(k)
|
d.pop(k)
|
||||||
parent = Blender.Object.Get(objname).getParent()
|
parent = Object.Get(objname).getParent()
|
||||||
if parent: kids_dict[parent.name] -= 1
|
if parent: kids_dict[parent.name] -= 1
|
||||||
REPORT_DATA['noexport'].append(objname)
|
REPORT_DATA['noexport'].append(objname)
|
||||||
continue
|
continue
|
||||||
if klen:
|
if klen:
|
||||||
self.traverse_dict(dict[k])
|
self.traverse_dict(d[k])
|
||||||
exp_objs.insert(0, objname)
|
exp_objs.insert(0, objname)
|
||||||
else:
|
else:
|
||||||
if k.find('Em', 0) == 0: # Empty w/o children
|
if k.find('Em', 0) == 0: # Empty w/o children
|
||||||
dict.pop(k)
|
d.pop(k)
|
||||||
parent = Blender.Object.Get(objname).getParent()
|
parent = Object.Get(objname).getParent()
|
||||||
if parent: kids_dict[parent.name] -= 1
|
if parent: kids_dict[parent.name] -= 1
|
||||||
else:
|
else:
|
||||||
exp_objs.insert(0, objname)
|
exp_objs.insert(0, objname)
|
||||||
@ -440,6 +484,7 @@ class AC3DExport: # the ac3d exporter part
|
|||||||
for k in keys:
|
for k in keys:
|
||||||
faces = tex_dict[k]
|
faces = tex_dict[k]
|
||||||
foo_meshes.append(FooMesh(k, faces, mesh))
|
foo_meshes.append(FooMesh(k, faces, mesh))
|
||||||
|
foo_meshes[0].edges = get_loose_edges(mesh)
|
||||||
return foo_meshes
|
return foo_meshes
|
||||||
return [mesh]
|
return [mesh]
|
||||||
|
|
||||||
@ -449,17 +494,37 @@ class AC3DExport: # the ac3d exporter part
|
|||||||
if not name: name = obj.name
|
if not name: name = obj.name
|
||||||
self.name(name)
|
self.name(name)
|
||||||
if not SKIP_DATA:
|
if not SKIP_DATA:
|
||||||
self.data(len(mesh.name), mesh.name)
|
meshname = obj.getData(name_only = True)
|
||||||
texline = self.texture(mesh.faces)
|
self.data(len(meshname), meshname)
|
||||||
if texline: file.write(texline)
|
if mesh.faceUV:
|
||||||
|
texline = self.texture(mesh.faces)
|
||||||
|
if texline: file.write(texline)
|
||||||
if AC3D_4:
|
if AC3D_4:
|
||||||
self.crease(mesh.getMaxSmoothAngle())
|
self.crease(mesh.degr)
|
||||||
self.numvert(mesh.verts, obj.getMatrix())
|
|
||||||
self.numsurf(mesh.faces, mesh.hasFaceUV(), foomesh)
|
|
||||||
|
|
||||||
def MATERIALS(self, meshlist):
|
# If exporting using local coordinates, children object coordinates should not be
|
||||||
|
# transformed to ac3d's coordinate system, since that will be accounted for in
|
||||||
|
# their topmost parents (the parents w/o parents) transformations.
|
||||||
|
if not GLOBAL_COORDS:
|
||||||
|
# We hold parents in a list, so they also don't get transformed,
|
||||||
|
# because for each parent we create an ac3d group to hold both the
|
||||||
|
# parent and its children.
|
||||||
|
if obj.name not in self.parents_list:
|
||||||
|
localmatrix = obj.getMatrix('localspace')
|
||||||
|
if not obj.getParent():
|
||||||
|
localmatrix *= BLEND_TO_AC3D_MATRIX
|
||||||
|
self.rot(localmatrix.rotationPart())
|
||||||
|
self.loc(localmatrix.translationPart())
|
||||||
|
matrix = None
|
||||||
|
else:
|
||||||
|
matrix = obj.getMatrix() * BLEND_TO_AC3D_MATRIX
|
||||||
|
|
||||||
|
self.numvert(mesh.verts, matrix)
|
||||||
|
self.numsurf(mesh, foomesh)
|
||||||
|
|
||||||
|
def MATERIALS(self, meshlist, me):
|
||||||
for meobj in meshlist:
|
for meobj in meshlist:
|
||||||
me = meobj.getData()
|
me.getFromObject(meobj)
|
||||||
mat = me.materials
|
mat = me.materials
|
||||||
mbuf = []
|
mbuf = []
|
||||||
mlist = self.mlist
|
mlist = self.mlist
|
||||||
@ -469,23 +534,24 @@ class AC3DExport: # the ac3d exporter part
|
|||||||
mlist.index(name)
|
mlist.index(name)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
mlist.append(name)
|
mlist.append(name)
|
||||||
M = Blender.Material.Get(name)
|
M = Material.Get(name)
|
||||||
material = 'MATERIAL "%s"' % name
|
material = 'MATERIAL "%s"' % name
|
||||||
mirCol = "%s %s %s" % (Round(M.mirCol[0]), Round(M.mirCol[1]),
|
mirCol = "%s %s %s" % (Round_s(M.mirCol[0]), Round_s(M.mirCol[1]),
|
||||||
Round(M.mirCol[2]))
|
Round_s(M.mirCol[2]))
|
||||||
rgb = "rgb %s %s %s" % (Round(M.R), Round(M.G), Round(M.B))
|
rgb = "rgb %s %s %s" % (Round_s(M.R), Round_s(M.G), Round_s(M.B))
|
||||||
amb = "amb %s %s %s" % (Round(M.amb), Round(M.amb), Round(M.amb))
|
ambval = Round_s(M.amb)
|
||||||
spec = "spec %s %s %s" % (Round(M.specCol[0]),
|
amb = "amb %s %s %s" % (ambval, ambval, ambval)
|
||||||
Round(M.specCol[1]), Round(M.specCol[2]))
|
spec = "spec %s %s %s" % (Round_s(M.specCol[0]),
|
||||||
|
Round_s(M.specCol[1]), Round_s(M.specCol[2]))
|
||||||
if AC3D_4:
|
if AC3D_4:
|
||||||
emit = Round(M.emit)
|
emit = Round_s(M.emit)
|
||||||
emis = "emis %s %s %s" % (emit, emit, emit)
|
emis = "emis %s %s %s" % (emit, emit, emit)
|
||||||
shival = int(M.spec * 64)
|
shival = int(M.spec * 64)
|
||||||
else:
|
else:
|
||||||
emis = "emis 0 0 0"
|
emis = "emis 0 0 0"
|
||||||
shival = 72
|
shival = 72
|
||||||
shi = "shi %s" % shival
|
shi = "shi %s" % shival
|
||||||
trans = "trans %s" % (Round(1 - M.alpha))
|
trans = "trans %s" % (Round_s(1 - M.alpha))
|
||||||
if MIRCOL_AS_AMB:
|
if MIRCOL_AS_AMB:
|
||||||
amb = "amb %s" % mirCol
|
amb = "amb %s" % mirCol
|
||||||
if MIRCOL_AS_EMIS:
|
if MIRCOL_AS_EMIS:
|
||||||
@ -516,12 +582,12 @@ class AC3DExport: # the ac3d exporter part
|
|||||||
tex = f.image.name
|
tex = f.image.name
|
||||||
break
|
break
|
||||||
if tex:
|
if tex:
|
||||||
image = Blender.Image.Get(tex)
|
image = Image.Get(tex)
|
||||||
texfname = image.filename
|
texfname = image.filename
|
||||||
if SET_TEX_DIR:
|
if SET_TEX_DIR:
|
||||||
texfname = Blender.sys.basename(texfname)
|
texfname = bsys.basename(texfname)
|
||||||
if TEX_DIR:
|
if TEX_DIR:
|
||||||
texfname = Blender.sys.join(TEX_DIR, texfname)
|
texfname = bsys.join(TEX_DIR, texfname)
|
||||||
buf = 'texture "%s"\n' % texfname
|
buf = 'texture "%s"\n' % texfname
|
||||||
xrep = image.xrep
|
xrep = image.xrep
|
||||||
yrep = image.yrep
|
yrep = image.yrep
|
||||||
@ -530,41 +596,65 @@ class AC3DExport: # the ac3d exporter part
|
|||||||
|
|
||||||
def rot(self, matrix):
|
def rot(self, matrix):
|
||||||
rot = ''
|
rot = ''
|
||||||
not_I = 0
|
not_I = 0 # not identity
|
||||||
|
matstr = []
|
||||||
for i in [0, 1, 2]:
|
for i in [0, 1, 2]:
|
||||||
r = map(Round, matrix[i])
|
r = map(Round_s, matrix[i])
|
||||||
not_I += (r[0] != '0.0')+(r[1] != '0.0')+(r[2] != '0.0')
|
not_I += (r[0] != '0')+(r[1] != '0')+(r[2] != '0')
|
||||||
not_I -= (r[i] == '1.0')
|
not_I -= (r[i] == '1')
|
||||||
for j in [0, 1, 2]:
|
for j in [0, 1, 2]:
|
||||||
rot = "%s %s" % (rot, r[j])
|
matstr.append(' %s' % r[j])
|
||||||
if not_I:
|
if not_I: # no need to write identity
|
||||||
self.file.write('rot %s\n' % rot.strip())
|
self.file.write('rot%s\n' % "".join(matstr))
|
||||||
|
|
||||||
def loc(self, loc):
|
def loc(self, loc):
|
||||||
loc = map(Round, loc)
|
loc = map(Round_s, loc)
|
||||||
if loc[0] or loc[1] or loc[2]:
|
if loc != ['0', '0', '0']: # no need to write default
|
||||||
self.file.write('loc %s %s %s\n' % (loc[0], loc[1], loc[2]))
|
self.file.write('loc %s %s %s\n' % (loc[0], loc[1], loc[2]))
|
||||||
|
|
||||||
def crease(self, crease):
|
def crease(self, crease):
|
||||||
self.file.write('crease %s\n' % crease)
|
self.file.write('crease %f\n' % crease)
|
||||||
|
|
||||||
def numvert(self, verts, matrix):
|
def numvert(self, verts, matrix):
|
||||||
file = self.file
|
file = self.file
|
||||||
file.write("numvert %s\n" % len(verts))
|
nvstr = []
|
||||||
m = matrix * acmatrix
|
nvstr.append("numvert %s\n" % len(verts))
|
||||||
verts = transform_verts(verts, m)
|
|
||||||
for v in verts:
|
|
||||||
v0, v1, v2 = Round(v[0]), Round(v[1]), Round(v[2])
|
|
||||||
file.write("%s %s %s\n" % (v0, v1, v2))
|
|
||||||
|
|
||||||
def numsurf(self, faces, hasFaceUV, foomesh = False):
|
if matrix:
|
||||||
|
verts = transform_verts(verts, matrix)
|
||||||
|
for v in verts:
|
||||||
|
v = map (Round_s, v)
|
||||||
|
nvstr.append("%s %s %s\n" % (v[0], v[1], v[2]))
|
||||||
|
else:
|
||||||
|
for v in verts:
|
||||||
|
v = map(Round_s, v.co)
|
||||||
|
nvstr.append("%s %s %s\n" % (v[0], v[1], v[2]))
|
||||||
|
|
||||||
|
file.write("".join(nvstr))
|
||||||
|
|
||||||
|
def numsurf(self, mesh, foomesh = False):
|
||||||
|
|
||||||
|
global MATIDX_ERROR
|
||||||
|
|
||||||
|
# local vars are faster and so better in tight loops
|
||||||
|
lc_ADD_DEFAULT_MAT = ADD_DEFAULT_MAT
|
||||||
|
lc_MATIDX_ERROR = MATIDX_ERROR
|
||||||
|
lc_PER_FACE_1_OR_2_SIDED = PER_FACE_1_OR_2_SIDED
|
||||||
|
lc_FACE_TWOSIDED = FACE_TWOSIDED
|
||||||
|
lc_MESH_TWOSIDED = MESH_TWOSIDED
|
||||||
|
|
||||||
|
faces = mesh.faces
|
||||||
|
hasFaceUV = mesh.faceUV
|
||||||
|
if foomesh:
|
||||||
|
looseEdges = mesh.looseEdges
|
||||||
|
else:
|
||||||
|
looseEdges = get_loose_edges(mesh)
|
||||||
|
|
||||||
global ADD_DEFAULT_MAT, MATIDX_ERROR
|
|
||||||
file = self.file
|
file = self.file
|
||||||
|
|
||||||
file.write("numsurf %s\n" % len(faces))
|
file.write("numsurf %s\n" % (len(faces) + len(looseEdges)))
|
||||||
|
|
||||||
if not foomesh: verts = self.mesh.verts
|
if not foomesh: verts = list(self.mesh.verts)
|
||||||
|
|
||||||
mlist = self.mlist
|
mlist = self.mlist
|
||||||
omlist = {}
|
omlist = {}
|
||||||
@ -573,33 +663,33 @@ class AC3DExport: # the ac3d exporter part
|
|||||||
for i in range(len(objmats)):
|
for i in range(len(objmats)):
|
||||||
objmats[i] = objmats[i].name
|
objmats[i] = objmats[i].name
|
||||||
for f in faces:
|
for f in faces:
|
||||||
m_idx = f.materialIndex
|
m_idx = f.mat
|
||||||
try:
|
try:
|
||||||
m_idx = mlist.index(objmats[m_idx])
|
m_idx = mlist.index(objmats[m_idx])
|
||||||
except IndexError:
|
except IndexError:
|
||||||
if not MATIDX_ERROR:
|
if not lc_MATIDX_ERROR:
|
||||||
rdat = REPORT_DATA['warns']
|
rdat = REPORT_DATA['warns']
|
||||||
rdat.append("Object %s" % self.obj.name)
|
rdat.append("Object %s" % self.obj.name)
|
||||||
rdat.append("has at least one material *index* assigned but not")
|
rdat.append("has at least one material *index* assigned but not")
|
||||||
rdat.append("defined (not linked to an existing material).")
|
rdat.append("defined (not linked to an existing material).")
|
||||||
rdat.append("Result: some faces may be exported with a wrong color.")
|
rdat.append("Result: some faces may be exported with a wrong color.")
|
||||||
rdat.append("You can link materials in the Edit Buttons window (F9).")
|
rdat.append("You can assign materials in the Edit Buttons window (F9).")
|
||||||
elif not matidx_error_told:
|
elif not matidx_error_told:
|
||||||
midxmsg = "- Same for object %s." % self.obj.name
|
midxmsg = "- Same for object %s." % self.obj.name
|
||||||
REPORT_DATA['warns'].append(midxmsg)
|
REPORT_DATA['warns'].append(midxmsg)
|
||||||
MATIDX_ERROR += 1
|
lc_MATIDX_ERROR += 1
|
||||||
matidx_error_told = 1
|
matidx_error_told = 1
|
||||||
m_idx = 0
|
m_idx = 0
|
||||||
refs = len(f)
|
refs = len(f)
|
||||||
flaglow = (refs == 2) << 1
|
flaglow = 0 # polygon
|
||||||
if PER_FACE_1_OR_2_SIDED: # per face attribute
|
if lc_PER_FACE_1_OR_2_SIDED and hasFaceUV: # per face attribute
|
||||||
two_side = f.mode & Blender.NMesh.FaceModes['TWOSIDE']
|
two_side = f.mode & lc_FACE_TWOSIDED
|
||||||
else: # global, for the whole mesh
|
else: # global, for the whole mesh
|
||||||
two_side = self.mesh.mode & Blender.NMesh.Modes['TWOSIDED']
|
two_side = self.mesh.mode & lc_MESH_TWOSIDED
|
||||||
two_side = (two_side > 0) << 1
|
two_side = (two_side > 0) << 1
|
||||||
flaghigh = f.smooth | two_side
|
flaghigh = f.smooth | two_side
|
||||||
surfstr = "SURF 0x%d%d\n" % (flaghigh, flaglow)
|
surfstr = "SURF 0x%d%d\n" % (flaghigh, flaglow)
|
||||||
if ADD_DEFAULT_MAT and objmats: m_idx += 1
|
if lc_ADD_DEFAULT_MAT and objmats: m_idx += 1
|
||||||
matstr = "mat %s\n" % m_idx
|
matstr = "mat %s\n" % m_idx
|
||||||
refstr = "refs %s\n" % refs
|
refstr = "refs %s\n" % refs
|
||||||
u, v, vi = 0, 0, 0
|
u, v, vi = 0, 0, 0
|
||||||
@ -625,6 +715,24 @@ class AC3DExport: # the ac3d exporter part
|
|||||||
|
|
||||||
file.write("%s%s%s%s" % (surfstr, matstr, refstr, fvstr))
|
file.write("%s%s%s%s" % (surfstr, matstr, refstr, fvstr))
|
||||||
|
|
||||||
|
for e in looseEdges:
|
||||||
|
fvstr = []
|
||||||
|
#flaglow = 2 # 1 = closed line, 2 = line
|
||||||
|
#flaghigh = 0
|
||||||
|
#surfstr = "SURF 0x%d%d\n" % (flaghigh, flaglow)
|
||||||
|
surfstr = "SURF 0x02\n"
|
||||||
|
|
||||||
|
fvstr.append("%d 0 0\n" % verts.index(e.v1))
|
||||||
|
fvstr.append("%d 0 0\n" % verts.index(e.v2))
|
||||||
|
fvstr = "".join(fvstr)
|
||||||
|
|
||||||
|
matstr = "mat 0\n" # for now, use first material
|
||||||
|
refstr = "refs 2\n" # 2 verts
|
||||||
|
|
||||||
|
file.write("%s%s%s%s" % (surfstr, matstr, refstr, fvstr))
|
||||||
|
|
||||||
|
MATIDX_ERROR = lc_MATIDX_ERROR
|
||||||
|
|
||||||
# End of Class AC3DExport
|
# End of Class AC3DExport
|
||||||
|
|
||||||
from Blender.Window import FileSelector
|
from Blender.Window import FileSelector
|
||||||
@ -687,7 +795,9 @@ def fs_callback(filename):
|
|||||||
|
|
||||||
|
|
||||||
# -- End of definitions
|
# -- End of definitions
|
||||||
|
|
||||||
scn = Blender.Scene.GetCurrent()
|
scn = Blender.Scene.GetCurrent()
|
||||||
|
|
||||||
if ONLY_SELECTED:
|
if ONLY_SELECTED:
|
||||||
OBJS = list(scn.objects.context)
|
OBJS = list(scn.objects.context)
|
||||||
else:
|
else:
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
""" Registration info for Blender menus:
|
""" Registration info for Blender menus:
|
||||||
Name: 'AC3D (.ac)...'
|
Name: 'AC3D (.ac)...'
|
||||||
Blender: 236
|
Blender: 242
|
||||||
Group: 'Import'
|
Group: 'Import'
|
||||||
Tip: 'Import an AC3D (.ac) file.'
|
Tip: 'Import an AC3D (.ac) file.'
|
||||||
"""
|
"""
|
||||||
@ -10,7 +10,7 @@ Tip: 'Import an AC3D (.ac) file.'
|
|||||||
__author__ = "Willian P. Germano"
|
__author__ = "Willian P. Germano"
|
||||||
__url__ = ("blender", "elysiun", "AC3D's homepage, http://www.ac3d.org",
|
__url__ = ("blender", "elysiun", "AC3D's homepage, http://www.ac3d.org",
|
||||||
"PLib 3d gaming lib, http://plib.sf.net")
|
"PLib 3d gaming lib, http://plib.sf.net")
|
||||||
__version__ = "2.36a 2005-12-04"
|
__version__ = "2.43 2007-01-14"
|
||||||
|
|
||||||
__bpydoc__ = """\
|
__bpydoc__ = """\
|
||||||
This script imports AC3D models into Blender.
|
This script imports AC3D models into Blender.
|
||||||
@ -29,8 +29,6 @@ Known issues:<br>
|
|||||||
None.
|
None.
|
||||||
|
|
||||||
Config Options:<br>
|
Config Options:<br>
|
||||||
- group (toggle): if "on", grouped objects in the .ac file are parented to
|
|
||||||
Empties.
|
|
||||||
- textures dir (string): if non blank, when imported texture paths are
|
- textures dir (string): if non blank, when imported texture paths are
|
||||||
wrong in the .ac file, Blender will also look for them at this dir.
|
wrong in the .ac file, Blender will also look for them at this dir.
|
||||||
|
|
||||||
@ -43,13 +41,13 @@ users can configure (see config options above).
|
|||||||
# $Id$
|
# $Id$
|
||||||
#
|
#
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# AC3DImport version 2.36a Dec 04, 2005
|
# AC3DImport version 2.43 Jan 04, 2007
|
||||||
# Program versions: Blender 2.36+ and AC3Db files (means version 0xb)
|
# Program versions: Blender 2.43 and AC3Db files (means version 0xb)
|
||||||
# changed: fixed a bug: error on 1 vertex "closed" polylines
|
# changed: updated for new Blender version, Mesh module
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# ***** BEGIN GPL LICENSE BLOCK *****
|
# ***** BEGIN GPL LICENSE BLOCK *****
|
||||||
#
|
#
|
||||||
# Copyright (C) 2005: Willian P. Germano, wgermano _at_ ig.com.br
|
# Copyright (C) 2004-2007: Willian P. Germano, wgermano _at_ ig.com.br
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
@ -74,33 +72,30 @@ users can configure (see config options above).
|
|||||||
# fixing. Avoiding or triangulating concave n-gons in AC3D is a simple way to
|
# fixing. Avoiding or triangulating concave n-gons in AC3D is a simple way to
|
||||||
# avoid problems.
|
# avoid problems.
|
||||||
|
|
||||||
|
from math import radians
|
||||||
|
|
||||||
import Blender
|
import Blender
|
||||||
from Blender import Registry
|
from Blender import Scene, Object, Mesh, Lamp, Registry, sys as bsys, Window, Image, Material
|
||||||
from Blender.sys import dirsep
|
from Blender.sys import dirsep
|
||||||
|
from Blender.Mathutils import Vector, Matrix, Euler
|
||||||
|
|
||||||
# Default folder for AC3D textures, to override wrong paths, change to your
|
# Default folder for AC3D textures, to override wrong paths, change to your
|
||||||
# liking or leave as "":
|
# liking or leave as "":
|
||||||
TEXTURES_DIR = ""
|
TEXTURES_DIR = ""
|
||||||
|
|
||||||
# Set 'GROUP' to True to make Blender group imported objects using Empties,
|
|
||||||
# to reproduce the object hierarchy in the .ac file
|
|
||||||
GROUP = False
|
|
||||||
|
|
||||||
tooltips = {
|
tooltips = {
|
||||||
'TEXTURES_DIR': 'additional dir to look for missing textures',
|
'TEXTURES_DIR': 'additional dir to look for missing textures'
|
||||||
'GROUP': 'mimick grouping information by parenting grouped meshes to empties'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def update_registry():
|
def update_registry():
|
||||||
global GROUP, TEXTURES_DIR
|
global TEXTURES_DIR
|
||||||
rd = dict([('GROUP', GROUP), ('TEXTURES_DIR', TEXTURES_DIR)])
|
rd = dict([('TEXTURES_DIR', TEXTURES_DIR)])
|
||||||
Registry.SetKey('ac3d_import', rd, True)
|
Registry.SetKey('ac3d_import', rd, True)
|
||||||
|
|
||||||
rd = Registry.GetKey('ac3d_import', True)
|
rd = Registry.GetKey('ac3d_import', True)
|
||||||
|
|
||||||
if rd:
|
if rd:
|
||||||
TEXTURES_DIR = rd['TEXTURES_DIR']
|
TEXTURES_DIR = rd['TEXTURES_DIR']
|
||||||
GROUP = rd['GROUP']
|
|
||||||
else: update_registry()
|
else: update_registry()
|
||||||
|
|
||||||
if TEXTURES_DIR:
|
if TEXTURES_DIR:
|
||||||
@ -119,11 +114,31 @@ if rd:
|
|||||||
|
|
||||||
errmsg = ""
|
errmsg = ""
|
||||||
|
|
||||||
|
# Matrix to align ac3d's coordinate system with Blender's one,
|
||||||
|
# it's a -90 degrees rotation around the x axis:
|
||||||
|
AC_TO_BLEND_MATRIX = Matrix([1, 0, 0], [0, 0, 1], [0, -1, 0])
|
||||||
|
|
||||||
|
AC_WORLD = 0
|
||||||
|
AC_GROUP = 1
|
||||||
|
AC_POLY = 2
|
||||||
|
AC_LIGHT = 3
|
||||||
|
AC_OB_TYPES = {
|
||||||
|
'world': AC_WORLD,
|
||||||
|
'group': AC_GROUP,
|
||||||
|
'poly': AC_POLY,
|
||||||
|
'light': AC_LIGHT
|
||||||
|
}
|
||||||
|
|
||||||
def inform(msg):
|
def inform(msg):
|
||||||
global VERBOSE
|
global VERBOSE
|
||||||
if VERBOSE: print msg
|
if VERBOSE: print msg
|
||||||
|
|
||||||
|
def euler_in_radians(eul):
|
||||||
|
"Used while there's a bug in the BPY API"
|
||||||
|
eul.x = radians(eul.x)
|
||||||
|
eul.y = radians(eul.y)
|
||||||
|
eul.z = radians(eul.z)
|
||||||
|
return eul
|
||||||
|
|
||||||
class Obj:
|
class Obj:
|
||||||
|
|
||||||
@ -135,23 +150,31 @@ class Obj:
|
|||||||
self.tex = ''
|
self.tex = ''
|
||||||
self.texrep = [1,1]
|
self.texrep = [1,1]
|
||||||
self.texoff = None
|
self.texoff = None
|
||||||
self.loc = [0, 0, 0]
|
self.loc = []
|
||||||
self.rot = []
|
self.rot = []
|
||||||
|
self.size = []
|
||||||
self.crease = 30
|
self.crease = 30
|
||||||
self.vlist = []
|
self.vlist = []
|
||||||
self.flist = []
|
self.flist_cfg = []
|
||||||
|
self.flist_v = []
|
||||||
|
self.flist_uv = []
|
||||||
|
self.elist = []
|
||||||
self.matlist = []
|
self.matlist = []
|
||||||
self.kids = 0
|
self.kids = 0
|
||||||
|
|
||||||
|
self.bl_obj = None # the actual Blender object created from this data
|
||||||
|
|
||||||
class AC3DImport:
|
class AC3DImport:
|
||||||
|
|
||||||
def __init__(self, filename):
|
def __init__(self, filename):
|
||||||
|
|
||||||
global errmsg
|
global errmsg
|
||||||
|
|
||||||
|
self.scene = Scene.GetCurrent()
|
||||||
|
|
||||||
self.i = 0
|
self.i = 0
|
||||||
errmsg = ''
|
errmsg = ''
|
||||||
self.importdir = Blender.sys.dirname(filename)
|
self.importdir = bsys.dirname(filename)
|
||||||
try:
|
try:
|
||||||
file = open(filename, 'r')
|
file = open(filename, 'r')
|
||||||
except IOError, (errno, strerror):
|
except IOError, (errno, strerror):
|
||||||
@ -187,8 +210,7 @@ class AC3DImport:
|
|||||||
|
|
||||||
self.objlist = []
|
self.objlist = []
|
||||||
self.mlist = []
|
self.mlist = []
|
||||||
self.dads = []
|
self.kidsnumlist = []
|
||||||
self.kids = []
|
|
||||||
self.dad = None
|
self.dad = None
|
||||||
|
|
||||||
self.lines = file.readlines()
|
self.lines = file.readlines()
|
||||||
@ -199,12 +221,12 @@ class AC3DImport:
|
|||||||
self.testAC3DImport()
|
self.testAC3DImport()
|
||||||
|
|
||||||
def parse_obj(self, value):
|
def parse_obj(self, value):
|
||||||
if self.kids:
|
if self.kidsnumlist:
|
||||||
while not self.kids[-1]:
|
while not self.kidsnumlist[-1]:
|
||||||
self.kids.pop()
|
self.kidsnumlist.pop()
|
||||||
self.dad = self.dad.dad
|
self.dad = self.dad.dad
|
||||||
self.kids[-1] -= 1
|
self.kidsnumlist[-1] -= 1
|
||||||
new = Obj(value)
|
new = Obj(AC_OB_TYPES[value])
|
||||||
new.dad = self.dad
|
new.dad = self.dad
|
||||||
new.name = value
|
new.name = value
|
||||||
self.objlist.append(new)
|
self.objlist.append(new)
|
||||||
@ -212,7 +234,7 @@ class AC3DImport:
|
|||||||
def parse_kids(self, value):
|
def parse_kids(self, value):
|
||||||
kids = int(value)
|
kids = int(value)
|
||||||
if kids:
|
if kids:
|
||||||
self.kids.append(kids)
|
self.kidsnumlist.append(kids)
|
||||||
self.dad = self.objlist[-1]
|
self.dad = self.objlist[-1]
|
||||||
self.objlist[-1].kids = kids
|
self.objlist[-1].kids = kids
|
||||||
|
|
||||||
@ -269,23 +291,24 @@ class AC3DImport:
|
|||||||
|
|
||||||
def parse_rot(self, trash):
|
def parse_rot(self, trash):
|
||||||
i = self.i - 1
|
i = self.i - 1
|
||||||
|
ob = self.objlist[-1]
|
||||||
rot = self.lines[i].split(' ', 1)[1]
|
rot = self.lines[i].split(' ', 1)[1]
|
||||||
rot = map(float, rot.split())
|
rot = map(float, rot.split())
|
||||||
self.objlist[-1].rot = rot
|
matrix = Matrix(rot[:3], rot[3:6], rot[6:])
|
||||||
|
ob.rot = matrix
|
||||||
|
size = matrix.scalePart() # vector
|
||||||
|
ob.size = size
|
||||||
|
|
||||||
def parse_loc(self, trash):
|
def parse_loc(self, trash):
|
||||||
i = self.i - 1
|
i = self.i - 1
|
||||||
loc = self.lines[i].split(' ', 1)[1]
|
loc = self.lines[i].split(' ', 1)[1]
|
||||||
loc = map(float, loc.split())
|
loc = map(float, loc.split())
|
||||||
self.objlist[-1].loc = loc
|
self.objlist[-1].loc = Vector(loc)
|
||||||
|
|
||||||
def parse_crease(self, value):
|
def parse_crease(self, value):
|
||||||
# AC3D: range is [0.0, 180.0]; Blender: [1, 80]
|
# AC3D: range is [0.0, 180.0]; Blender: [1, 80]
|
||||||
try:
|
value = float(value)
|
||||||
value = int(value)
|
self.objlist[-1].crease = int(value)
|
||||||
except ValueError:
|
|
||||||
value = int(float(value)) # duh
|
|
||||||
self.objlist[-1].crease = value
|
|
||||||
|
|
||||||
def parse_vert(self, value):
|
def parse_vert(self, value):
|
||||||
i = self.i
|
i = self.i
|
||||||
@ -303,28 +326,6 @@ class AC3DImport:
|
|||||||
|
|
||||||
self.i = i
|
self.i = i
|
||||||
|
|
||||||
rot = obj.rot
|
|
||||||
if rot:
|
|
||||||
nv = len(vlist)
|
|
||||||
for j in range(nv):
|
|
||||||
v = vlist[j]
|
|
||||||
t = [0,0,0]
|
|
||||||
t[0] = rot[0]*v[0] + rot[3]*v[1] + rot[6]*v[2]
|
|
||||||
t[1] = rot[1]*v[0] + rot[4]*v[1] + rot[7]*v[2]
|
|
||||||
t[2] = rot[2]*v[0] + rot[5]*v[1] + rot[8]*v[2]
|
|
||||||
vlist[j] = t
|
|
||||||
|
|
||||||
loc = obj.loc
|
|
||||||
dad = obj.dad
|
|
||||||
while dad:
|
|
||||||
for j in [0, 1, 2]:
|
|
||||||
loc[j] += dad.loc[j]
|
|
||||||
dad = dad.dad
|
|
||||||
|
|
||||||
for v in vlist:
|
|
||||||
for j in [0, 1, 2]:
|
|
||||||
v[j] += loc[j]
|
|
||||||
|
|
||||||
def parse_surf(self, value):
|
def parse_surf(self, value):
|
||||||
i = self.i
|
i = self.i
|
||||||
is_smooth = 0
|
is_smooth = 0
|
||||||
@ -333,6 +334,7 @@ class AC3DImport:
|
|||||||
obj = self.objlist[-1]
|
obj = self.objlist[-1]
|
||||||
matlist = obj.matlist
|
matlist = obj.matlist
|
||||||
numsurf = int(value)
|
numsurf = int(value)
|
||||||
|
NUMSURF = numsurf
|
||||||
|
|
||||||
while numsurf:
|
while numsurf:
|
||||||
flags = lines[i].split()
|
flags = lines[i].split()
|
||||||
@ -349,45 +351,51 @@ class AC3DImport:
|
|||||||
i += 3
|
i += 3
|
||||||
face = []
|
face = []
|
||||||
faces = []
|
faces = []
|
||||||
|
edges = []
|
||||||
fuv = []
|
fuv = []
|
||||||
|
fuvs = []
|
||||||
rfs = refs
|
rfs = refs
|
||||||
|
|
||||||
while rfs:
|
while rfs:
|
||||||
line = lines[i].split()
|
line = lines[i].split()
|
||||||
v = int(line[0])
|
v = int(line[0])
|
||||||
uv = [float(line[1]), float(line[2])]
|
uv = [float(line[1]), float(line[2])]
|
||||||
face.append([v, uv])
|
face.append(v)
|
||||||
|
fuv.append(Vector(uv))
|
||||||
rfs -= 1
|
rfs -= 1
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
if flaglow:
|
if flaglow: # it's a line or closed line, not a polygon
|
||||||
while len(face) >= 2:
|
while len(face) >= 2:
|
||||||
cut = face[:2]
|
cut = face[:2]
|
||||||
faces.append(cut)
|
edges.append(cut)
|
||||||
face = face[1:]
|
face = face[1:]
|
||||||
|
|
||||||
if flaglow == 1 and faces:
|
if flaglow == 1 and edges:
|
||||||
face = [faces[-1][-1], faces[0][0]]
|
face = [edges[-1][-1], edges[0][0]]
|
||||||
faces.append(face)
|
edges.append(face)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
while len(face) > 4:
|
while len(face) > 4:
|
||||||
cut = face[:4]
|
cut = face[:4]
|
||||||
|
cutuv = fuv[:4]
|
||||||
face = face[3:]
|
face = face[3:]
|
||||||
|
fuv = fuv[3:]
|
||||||
face.insert(0, cut[0])
|
face.insert(0, cut[0])
|
||||||
faces.append(cut)
|
fuv.insert(0, cutuv[0])
|
||||||
|
faces.append(cut)
|
||||||
|
fuvs.append(cutuv)
|
||||||
|
|
||||||
faces.append(face)
|
faces.append(face)
|
||||||
|
fuvs.append(fuv)
|
||||||
|
|
||||||
for f in faces:
|
obj.flist_cfg.extend([[mat, is_smooth, twoside]] * len(faces))
|
||||||
f.append(mat)
|
obj.flist_v.extend(faces)
|
||||||
f.append(is_smooth)
|
obj.flist_uv.extend(fuvs)
|
||||||
f.append(twoside)
|
obj.elist.extend(edges) # loose edges
|
||||||
self.objlist[-1].flist.append(f)
|
|
||||||
|
|
||||||
numsurf -= 1
|
numsurf -= 1
|
||||||
|
|
||||||
|
|
||||||
self.i = i
|
self.i = i
|
||||||
|
|
||||||
def parse_file(self):
|
def parse_file(self):
|
||||||
@ -408,14 +416,86 @@ class AC3DImport:
|
|||||||
i = self.i
|
i = self.i
|
||||||
line = lines[i].split()
|
line = lines[i].split()
|
||||||
|
|
||||||
|
# for each group of meshes we try to find one that can be used as
|
||||||
|
# parent of the group in Blender.
|
||||||
|
# If not found, we can use an Empty as parent.
|
||||||
|
def found_parent(self, groupname, olist):
|
||||||
|
l = [o for o in olist if o.type == AC_POLY \
|
||||||
|
and not o.kids and not o.rot and not o.loc]
|
||||||
|
if l:
|
||||||
|
if len(l) > 1:
|
||||||
|
for o in l:
|
||||||
|
if o.name == groupname:
|
||||||
|
return o
|
||||||
|
return l[0]
|
||||||
|
return None
|
||||||
|
|
||||||
|
def build_hierarchy(self):
|
||||||
|
blmatrix = AC_TO_BLEND_MATRIX
|
||||||
|
|
||||||
|
olist = self.objlist[1:]
|
||||||
|
olist.reverse()
|
||||||
|
|
||||||
|
newlist = []
|
||||||
|
|
||||||
|
for o in olist:
|
||||||
|
kids = o.kids
|
||||||
|
if kids:
|
||||||
|
children = newlist[-kids:]
|
||||||
|
newlist = newlist[:-kids]
|
||||||
|
if o.type == AC_GROUP:
|
||||||
|
parent = self.found_parent(o.name, children)
|
||||||
|
if parent:
|
||||||
|
children.remove(parent)
|
||||||
|
o.bl_obj = parent.bl_obj
|
||||||
|
else: # not found, use an empty
|
||||||
|
empty = Object.New('Empty', o.name)
|
||||||
|
self.scene.link(empty)
|
||||||
|
empty.select(True)
|
||||||
|
o.bl_obj = empty
|
||||||
|
|
||||||
|
bl_children = [c.bl_obj for c in children]
|
||||||
|
o.bl_obj.makeParent(bl_children, 0, 1)
|
||||||
|
for child in children:
|
||||||
|
if child.loc:
|
||||||
|
child.bl_obj.setLocation(child.loc)
|
||||||
|
if child.rot:
|
||||||
|
eul = euler_in_radians(child.rot.toEuler())
|
||||||
|
child.bl_obj.setEuler(eul)
|
||||||
|
if child.size:
|
||||||
|
child.bl_obj.size = child.size
|
||||||
|
|
||||||
|
newlist.append(o)
|
||||||
|
|
||||||
|
for o in newlist: # newlist now only has objs w/o parents
|
||||||
|
blob = o.bl_obj
|
||||||
|
if o.loc:
|
||||||
|
blob.setLocation(o.loc * blmatrix)
|
||||||
|
if o.size:
|
||||||
|
o.bl_obj.size = o.size
|
||||||
|
if not o.rot:
|
||||||
|
blob.setEuler([1.5707963267948966, 0, 0])
|
||||||
|
else:
|
||||||
|
matrix = o.rot * blmatrix
|
||||||
|
eul = euler_in_radians(matrix.toEuler())
|
||||||
|
blob.setEuler(eul)
|
||||||
|
|
||||||
def testAC3DImport(self):
|
def testAC3DImport(self):
|
||||||
global GROUP
|
|
||||||
scene = Blender.Scene.GetCurrent()
|
FACE_TWOSIDE = Mesh.FaceModes['TWOSIDE']
|
||||||
|
FACE_TEX = Mesh.FaceModes['TEX']
|
||||||
|
MESH_AUTOSMOOTH = Mesh.Modes['AUTOSMOOTH']
|
||||||
|
|
||||||
|
scene = self.scene
|
||||||
|
|
||||||
|
bl_images = {} # loaded texture images
|
||||||
|
|
||||||
|
objlist = self.objlist[1:] # skip 'world'
|
||||||
|
|
||||||
bmat = []
|
bmat = []
|
||||||
for mat in self.mlist:
|
for mat in self.mlist:
|
||||||
name = mat[0]
|
name = mat[0]
|
||||||
m = Blender.Material.New(name)
|
m = Material.New(name)
|
||||||
m.rgbCol = (mat[1][0], mat[1][1], mat[1][2])
|
m.rgbCol = (mat[1][0], mat[1][1], mat[1][2])
|
||||||
m.amb = mat[2]
|
m.amb = mat[2]
|
||||||
m.emit = mat[3]
|
m.emit = mat[3]
|
||||||
@ -424,102 +504,133 @@ class AC3DImport:
|
|||||||
m.alpha = mat[6]
|
m.alpha = mat[6]
|
||||||
bmat.append(m)
|
bmat.append(m)
|
||||||
|
|
||||||
for obj in self.objlist:
|
obj_idx = 0 # index of current obj in loop
|
||||||
if obj.type == 'world':
|
for obj in objlist:
|
||||||
|
if obj.type == AC_GROUP:
|
||||||
continue
|
continue
|
||||||
elif obj.type == 'group':
|
elif obj.type == AC_LIGHT:
|
||||||
if not GROUP: continue
|
light = Lamp.New('Lamp')
|
||||||
empty = Blender.Object.New('Empty')
|
object = scene.objects.new(light, obj.name)
|
||||||
empty.name = obj.name
|
object.select(True)
|
||||||
scene.link(empty)
|
obj.bl_obj = object
|
||||||
if self.dads:
|
if obj.data:
|
||||||
dadobj = Blender.Object.get(self.dads.pop())
|
light.name = obj.data
|
||||||
dadobj.makeParent([empty])
|
|
||||||
while obj.kids:
|
|
||||||
self.dads.append(empty.name)
|
|
||||||
obj.kids -= 1
|
|
||||||
continue
|
continue
|
||||||
mesh = Blender.NMesh.New()
|
|
||||||
|
# type AC_POLY:
|
||||||
|
|
||||||
|
# old .ac files used empty meshes as groups, convert to a real ac group
|
||||||
|
if not obj.vlist:
|
||||||
|
obj.type = AC_GROUP
|
||||||
|
continue
|
||||||
|
|
||||||
|
mesh = Mesh.New()
|
||||||
|
object = scene.objects.new(mesh, obj.name)
|
||||||
|
object.select(True)
|
||||||
|
obj.bl_obj = object
|
||||||
if obj.data: mesh.name = obj.data
|
if obj.data: mesh.name = obj.data
|
||||||
mesh.setMaxSmoothAngle(obj.crease) # will clamp to [1, 80]
|
mesh.degr = obj.crease # will auto clamp to [1, 80]
|
||||||
mesh.hasFaceUV(1)
|
|
||||||
|
|
||||||
tex = None
|
mesh.verts.extend(obj.vlist)
|
||||||
if obj.tex != '':
|
|
||||||
try:
|
|
||||||
tex = Blender.Image.Load(obj.tex)
|
|
||||||
# Commented because it's unnecessary:
|
|
||||||
#tex.xrep = int(obj.texrep[0])
|
|
||||||
#tex.yrep = int(obj.texrep[1])
|
|
||||||
except:
|
|
||||||
basetexname = Blender.sys.basename(obj.tex)
|
|
||||||
try:
|
|
||||||
obj.tex = self.importdir + '/' + basetexname
|
|
||||||
tex = Blender.Image.Load(obj.tex)
|
|
||||||
except:
|
|
||||||
try:
|
|
||||||
obj.tex = TEXTURES_DIR + basetexname
|
|
||||||
tex = Blender.Image.Load(obj.tex)
|
|
||||||
except:
|
|
||||||
inform("Couldn't load texture: %s" % basetexname)
|
|
||||||
|
|
||||||
for v in obj.vlist:
|
|
||||||
bvert = Blender.NMesh.Vert(v[0],v[1],v[2])
|
|
||||||
mesh.verts.append(bvert)
|
|
||||||
|
|
||||||
objmat_indices = []
|
objmat_indices = []
|
||||||
for mat in bmat:
|
for mat in bmat:
|
||||||
if bmat.index(mat) in obj.matlist:
|
if bmat.index(mat) in obj.matlist:
|
||||||
objmat_indices.append(bmat.index(mat))
|
objmat_indices.append(bmat.index(mat))
|
||||||
mesh.materials.append(mat)
|
mesh.materials += [mat]
|
||||||
for f in obj.flist:
|
|
||||||
twoside = f[-1]
|
for e in obj.elist:
|
||||||
is_smooth = f[-2]
|
mesh.edges.extend(e)
|
||||||
fmat = f[-3]
|
|
||||||
f=f[:-3]
|
mesh.faces.extend(obj.flist_v)
|
||||||
bface = Blender.NMesh.Face()
|
|
||||||
|
# checking if the .ac file had duplicate faces (Blender ignores them):
|
||||||
|
if len(mesh.faces) != len(obj.flist_v):
|
||||||
|
# it has, ugh. Let's clean the uv list:
|
||||||
|
lenfl = len(obj.flist_v)
|
||||||
|
flist = obj.flist_v
|
||||||
|
uvlist = obj.flist_uv
|
||||||
|
cfglist = obj.flist_cfg
|
||||||
|
for f in flist:
|
||||||
|
f.sort()
|
||||||
|
for fi in range(lenfl - 1):
|
||||||
|
if flist[fi] in flist[fi+1:]:
|
||||||
|
uvlist.pop(fi)
|
||||||
|
cfglist.pop(fi)
|
||||||
|
|
||||||
|
if obj.flist_v: mesh.faceUV = True
|
||||||
|
|
||||||
|
img = None
|
||||||
|
tex = None
|
||||||
|
if obj.tex != '' and mesh.faceUV:
|
||||||
|
baseimgname = bsys.basename(obj.tex)
|
||||||
|
if obj.tex in bl_images.keys():
|
||||||
|
img = bl_images[obj.txt]
|
||||||
|
tex = bl_textures[img]
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
img = Image.Load(obj.tex)
|
||||||
|
# Commented because it's unnecessary:
|
||||||
|
#img.xrep = int(obj.texrep[0])
|
||||||
|
#img.yrep = int(obj.texrep[1])
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
obj.tex = self.importdir + '/' + baseimgname
|
||||||
|
img = Image.Load(obj.tex)
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
obj.tex = TEXTURES_DIR + baseimgname
|
||||||
|
img = Image.Load(obj.tex)
|
||||||
|
except:
|
||||||
|
inform("Couldn't load texture: %s" % baseimgname)
|
||||||
|
if img:
|
||||||
|
bl_images[obj.tex] = img
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
for f in obj.flist_cfg:
|
||||||
|
fmat = f[0]
|
||||||
|
is_smooth = f[1]
|
||||||
|
twoside = f[2]
|
||||||
|
bface = mesh.faces[i]
|
||||||
bface.smooth = is_smooth
|
bface.smooth = is_smooth
|
||||||
if twoside: bface.mode |= Blender.NMesh.FaceModes['TWOSIDE']
|
if twoside: bface.mode |= FACE_TWOSIDE
|
||||||
if tex:
|
if img:
|
||||||
bface.mode |= Blender.NMesh.FaceModes['TEX']
|
bface.mode |= FACE_TEX
|
||||||
bface.image = tex
|
bface.image = img
|
||||||
bface.materialIndex = objmat_indices.index(fmat)
|
bface.mat = objmat_indices.index(fmat)
|
||||||
|
fuv = obj.flist_uv[i]
|
||||||
if obj.texoff:
|
if obj.texoff:
|
||||||
uoff = obj.texoff[0]
|
uoff = obj.texoff[0]
|
||||||
voff = obj.texoff[1]
|
voff = obj.texoff[1]
|
||||||
urep = obj.texrep[0]
|
urep = obj.texrep[0]
|
||||||
vrep = obj.texrep[1]
|
vrep = obj.texrep[1]
|
||||||
for vi in range(len(f)):
|
for uv in fuv:
|
||||||
f[vi][1][0] *= urep
|
uv[0] *= urep
|
||||||
f[vi][1][1] *= vrep
|
uv[1] *= vrep
|
||||||
f[vi][1][0] += uoff
|
uv[0] += uoff
|
||||||
f[vi][1][1] += voff
|
uv[1] += voff
|
||||||
|
|
||||||
for vi in range(len(f)):
|
mesh.faces[i].uv = fuv
|
||||||
bface.v.append(mesh.verts[f[vi][0]])
|
|
||||||
bface.uv.append((f[vi][1][0], f[vi][1][1]))
|
|
||||||
#mesh.faces.append(bface)
|
|
||||||
# quick hack, will switch from NMesh to Mesh later:
|
|
||||||
if len(bface.v) > 1: mesh.addFace(bface)
|
|
||||||
|
|
||||||
mesh.mode = 0
|
i += 1
|
||||||
object = Blender.NMesh.PutRaw(mesh)
|
|
||||||
object.setName(obj.name)
|
mesh.mode = MESH_AUTOSMOOTH
|
||||||
object.setEuler([1.5707963,0,0]) # align ac3d w/ Blender
|
|
||||||
if self.dads:
|
obj_idx += 1
|
||||||
dadobj = Blender.Object.get(self.dads.pop())
|
|
||||||
dadobj.makeParent([object])
|
self.build_hierarchy()
|
||||||
|
scene.update()
|
||||||
|
|
||||||
# End of class AC3DImport
|
# End of class AC3DImport
|
||||||
|
|
||||||
def filesel_callback(filename):
|
def filesel_callback(filename):
|
||||||
|
|
||||||
inform("Trying to import AC3D model(s) from %s ..." % filename)
|
inform("\nTrying to import AC3D model(s) from:\n%s ..." % filename)
|
||||||
Blender.Window.WaitCursor(1)
|
Window.WaitCursor(1)
|
||||||
starttime = Blender.sys.time()
|
starttime = bsys.time()
|
||||||
test = AC3DImport(filename)
|
test = AC3DImport(filename)
|
||||||
Blender.Window.WaitCursor(0)
|
Window.WaitCursor(0)
|
||||||
endtime = Blender.sys.time() - starttime
|
endtime = bsys.time() - starttime
|
||||||
inform('... done! Data imported in %.3f seconds.\n' % endtime)
|
inform('Done! Data imported in %.3f seconds.\n' % endtime)
|
||||||
|
|
||||||
Blender.Window.FileSelector(filesel_callback, "Import AC3D", "*.ac")
|
Window.FileSelector(filesel_callback, "Import AC3D", "*.ac")
|
||||||
|
Loading…
Reference in New Issue
Block a user