forked from speed-dreams/speed-dreams-code
add plugin ac3d import/export ac3d blender 2.6
git-svn-id: https://svn.code.sf.net/p/speed-dreams/code/trunk@5661 30fe4595-0a0c-4342-8851-515496e4dcbd Former-commit-id: 609d63390db22096c7a621e3169866b300d85361 Former-commit-id: 050b10d4a55fe2053716725c3061c921ae94cc68
This commit is contained in:
parent
2a659bb5a1
commit
718454b5b0
4 changed files with 1548 additions and 0 deletions
402
src/tools/blender/blender-2.6/io_scene_ac3d/AC3D.py
Normal file
402
src/tools/blender/blender-2.6/io_scene_ac3d/AC3D.py
Normal file
|
@ -0,0 +1,402 @@
|
|||
import bpy, os, shutil
|
||||
from math import radians, degrees
|
||||
from mathutils import Vector, Matrix
|
||||
|
||||
DEBUG = False
|
||||
|
||||
def TRACE(message):
|
||||
if DEBUG:
|
||||
print(message)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
class Object:
|
||||
'''
|
||||
Base class for an AC3D object.
|
||||
'''
|
||||
|
||||
def __init__( self,
|
||||
name,
|
||||
ob_type,
|
||||
bl_obj,
|
||||
export_config,
|
||||
local_transform ):
|
||||
'''
|
||||
Create a AC3D object from a blender object and it's children
|
||||
|
||||
@param name The name of the object
|
||||
@param ob_type The type of the object (world, poly, group)
|
||||
@param bl_obj The according blender object
|
||||
@param export_config Settings for export TODO move to export method?
|
||||
'''
|
||||
|
||||
self.export_config = export_config
|
||||
self.name = name.replace('"','') # quotes not allowed...
|
||||
self.type = ob_type
|
||||
self.bl_obj = bl_obj
|
||||
self.data = '' # custom data (eg. description)
|
||||
self.url = '' # custom url (use for whatever you want but never ever
|
||||
# put spaces into it)
|
||||
|
||||
if bl_obj:
|
||||
self.matrix_world = local_transform * bl_obj.matrix_world
|
||||
self.pos_abs = self.matrix_world.to_translation()
|
||||
else:
|
||||
self.matrix_world = local_transform
|
||||
self.pos_abs = None
|
||||
|
||||
self.children = []
|
||||
self.parent = None
|
||||
|
||||
def addChild( self,
|
||||
child ):
|
||||
if not isinstance(child, Object):
|
||||
raise Exception('addChild: can only add childs derived from Object')
|
||||
child.parent = self
|
||||
self.children.append(child)
|
||||
|
||||
def _parse( self, ac_mats, str_pre ):
|
||||
'''
|
||||
Override to process the blender mesh and add materials to ac_mats if
|
||||
needed
|
||||
'''
|
||||
pass
|
||||
|
||||
def parse( self, ac_mats, str_pre = '' ):
|
||||
TRACE("{0}+-({1}) {2}".format(str_pre, self.type, self.name))
|
||||
|
||||
self._parse(ac_mats, str_pre)
|
||||
|
||||
for child in self.children:
|
||||
child.parse(ac_mats, str_pre + ' ')
|
||||
|
||||
def _write( self, strm ):
|
||||
pass
|
||||
|
||||
def write( self, strm ):
|
||||
strm.write('OBJECT {0}\nname "{1}"\n'.format(self.type, self.name))
|
||||
|
||||
if len(self.data):
|
||||
strm.write('data {0}\n'.format(len(self.data)))
|
||||
strm.write('{0}\n'.format(self.data))
|
||||
|
||||
if len(self.url):
|
||||
strm.write('url {0}\n'.format(self.url))
|
||||
|
||||
if self.parent and self.pos_abs:
|
||||
# position relative to parent
|
||||
pos_rel = self.pos_abs - self.parent.matrix_world.to_translation()
|
||||
|
||||
location = self.export_config.global_matrix * pos_rel
|
||||
strm.write('loc {0:.7f} {1:.7f} {2:.7f}\n'.format(location[0], location[1], location[2]))
|
||||
|
||||
self._write(strm)
|
||||
strm.write('kids {0}\n'.format(len(self.children)))
|
||||
|
||||
for child in self.children:
|
||||
child.write(strm)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
class World (Object):
|
||||
'''
|
||||
Normally the root element is a world object
|
||||
'''
|
||||
def __init__( self,
|
||||
name,
|
||||
export_config,
|
||||
local_transform = Matrix() ):
|
||||
Object.__init__(self, name, 'world', None, export_config, local_transform)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
class Poly (Object):
|
||||
'''
|
||||
A polygon mesh
|
||||
'''
|
||||
def __init__( self,
|
||||
name,
|
||||
bl_obj,
|
||||
export_config,
|
||||
local_transform = Matrix() ):
|
||||
Object.__init__(self, name, 'poly', bl_obj, export_config, local_transform)
|
||||
|
||||
self.crease = None
|
||||
self.vertices = []
|
||||
self.surfaces = []
|
||||
self.tex_name = '' # texture name (filename of texture)
|
||||
self.tex_rep = [1,1] # texture repeat
|
||||
self.ac_mats = {} # Blender to AC3d index cross-reference
|
||||
|
||||
def _parse( self, ac_mats, str_pre ):
|
||||
|
||||
if self.bl_obj:
|
||||
TRACE('{0} ~ ({1}) {2}'.format( str_pre,
|
||||
self.bl_obj.type,
|
||||
self.bl_obj.data.name ))
|
||||
#if self.bl_obj.type == 'MESH':
|
||||
self._parseMesh(ac_mats)
|
||||
|
||||
def _parseMesh( self, ac_mats ):
|
||||
mesh = self.bl_obj.to_mesh(self.export_config.context.scene, True, 'PREVIEW')
|
||||
|
||||
self._parseMaterials(mesh, ac_mats)
|
||||
self._parseVertices(mesh)
|
||||
self._parseFaces(mesh)
|
||||
|
||||
for mod in self.bl_obj.modifiers:
|
||||
if mod.type=='EDGE_SPLIT':
|
||||
self.crease = degrees(mod.split_angle)
|
||||
break
|
||||
|
||||
if not self.crease:
|
||||
if mesh.use_auto_smooth:
|
||||
self.crease = degrees(mesh.auto_smooth_angle)
|
||||
else:
|
||||
self.crease = self.export_config.crease_angle
|
||||
|
||||
#bpy.data.meshes.remove(mesh)
|
||||
|
||||
def _parseMaterials( self, mesh, ac_mats ):
|
||||
'''
|
||||
Extract the materials from a blender mesh and create an id mapping from
|
||||
object material index to global AC3D material index
|
||||
'''
|
||||
mat_index = 0 # local material index
|
||||
for bl_mat in mesh.materials:
|
||||
if not bl_mat:
|
||||
continue
|
||||
ac_mat = Material(bl_mat.name, bl_mat, self.export_config)
|
||||
|
||||
mat_exists = False
|
||||
for mat in ac_mats:
|
||||
if mat.same_as(ac_mat):
|
||||
ac_mat = mat
|
||||
mat_exists = True
|
||||
break
|
||||
|
||||
if not mat_exists:
|
||||
ac_mats.append(ac_mat)
|
||||
|
||||
if not len(self.tex_name):
|
||||
for tex_slot in bl_mat.texture_slots:
|
||||
if tex_slot and tex_slot.texture_coords == 'UV':
|
||||
bl_tex = tex_slot.texture
|
||||
bl_im = bl_tex.image
|
||||
tex_name = bpy.path.basename(bl_im.filepath)
|
||||
export_tex = os.path.join(self.export_config.exportdir, tex_name)
|
||||
# TRACE('Exporting texture "{0}" to "{1}"'.format(bl_im.filepath, export_tex))
|
||||
# TODO: Optionally over-write existing textures
|
||||
if not os.path.exists(export_tex):
|
||||
if bl_im.packed_file:
|
||||
bl_im.file_format = 'PNG'
|
||||
bl_im.filepath = export_tex
|
||||
bl_im.unpack('WRITE_LOCAL')
|
||||
else:
|
||||
abs_path = bpy.path.abspath(bl_im.filepath)
|
||||
if not os.path.exists(abs_path):
|
||||
TRACE('Warning: Texture doesn\'t exists: {0}'.format(bl_im.filepath))
|
||||
else:
|
||||
shutil.copy(abs_path, export_tex)
|
||||
# else:
|
||||
# TRACE('File already exists "{0}"- not overwriting!'.format(tex_name))
|
||||
|
||||
self.tex_name = tex_name
|
||||
break
|
||||
|
||||
# Blender to AC3d index cross-reference
|
||||
# TRACE('Created Material {0} at index {1}'.format(ac_mats.index(ac_mat), mat_index))
|
||||
self.ac_mats[mat_index] = ac_mats.index(ac_mat)
|
||||
mat_index = mat_index + 1
|
||||
|
||||
def _parseVertices( self, mesh ):
|
||||
'''
|
||||
Extract the vertices from a blender mesh
|
||||
'''
|
||||
transform = self.export_config.global_matrix\
|
||||
* Matrix.Translation(-self.pos_abs)\
|
||||
* self.matrix_world
|
||||
self.vertices = [transform * v.co for v in mesh.vertices]
|
||||
|
||||
def _parseFaces( self, mesh ):
|
||||
'''
|
||||
Extract the faces from a blender mesh
|
||||
'''
|
||||
if len(mesh.uv_textures):
|
||||
uv_tex = mesh.tessface_uv_textures.active
|
||||
else:
|
||||
uv_tex = None
|
||||
|
||||
is_flipped = self.bl_obj.scale[0]\
|
||||
* self.bl_obj.scale[1]\
|
||||
* self.bl_obj.scale[2] < 0
|
||||
|
||||
for face_idx in range(len(mesh.tessfaces)):
|
||||
bl_face = mesh.tessfaces[face_idx]
|
||||
|
||||
if uv_tex:
|
||||
uv_coords = uv_tex.data[face_idx].uv[:]
|
||||
else:
|
||||
uv_coords = None
|
||||
|
||||
surf = self.Surface(self.export_config, bl_face, self.ac_mats, mesh.show_double_sided, is_flipped, uv_coords)
|
||||
self.surfaces.append(surf)
|
||||
|
||||
def _write( self, strm ):
|
||||
|
||||
if len(self.tex_name) > 0:
|
||||
strm.write('texture "{0}"\n'.format(self.tex_name))
|
||||
strm.write('texrep {0} {1}\n'.format(self.tex_rep[0], self.tex_rep[1]))
|
||||
|
||||
if len(self.vertices):
|
||||
strm.write('numvert {0}\n'.format(len(self.vertices)))
|
||||
for vert in self.vertices:
|
||||
strm.write('{0:.7f} {1:.7f} {2:.7f}\n'.format( vert[0],
|
||||
vert[1],
|
||||
vert[2] ))
|
||||
|
||||
if len(self.surfaces):
|
||||
strm.write('numsurf {0}\n'.format(len(self.surfaces)))
|
||||
for surf in self.surfaces:
|
||||
surf.write(strm)
|
||||
|
||||
# ------------------------------
|
||||
class Surface:
|
||||
def __init__( self,
|
||||
export_config,
|
||||
bl_face,
|
||||
ac_mats,
|
||||
is_two_sided,
|
||||
is_flipped,
|
||||
uv_coords ):
|
||||
self.export_config = export_config
|
||||
self.mat = 0 # material index for this surface
|
||||
self.bl_face = bl_face
|
||||
self.uv_coords = uv_coords
|
||||
self.is_two_sided = is_two_sided
|
||||
self.is_flipped = is_flipped
|
||||
self.ac_surf_flags = self.SurfaceFlags(0, False, True)
|
||||
|
||||
self.parse_blender_face(bl_face, ac_mats)
|
||||
|
||||
def write(self, ac_file):
|
||||
surf_flags = self.ac_surf_flags.getFlags()
|
||||
ac_file.write('SURF {0:#X}\n'.format(surf_flags))
|
||||
ac_file.write('mat {0}\n'.format(self.mat))
|
||||
ac_file.write('refs {0}\n'.format(len(self.bl_face.vertices)))
|
||||
|
||||
r = range(len(self.bl_face.vertices))
|
||||
if self.is_flipped:
|
||||
r = reversed(r)
|
||||
|
||||
if self.uv_coords:
|
||||
for n in r:
|
||||
surf_ref = self.bl_face.vertices[n]
|
||||
uv_ref = self.uv_coords[n]
|
||||
ac_file.write('{0} {1:.6f} {2:.6f}\n'.format(surf_ref, uv_ref[0], uv_ref[1]))
|
||||
else:
|
||||
for n in r:
|
||||
surf_ref = self.bl_face.vertices[n]
|
||||
ac_file.write('{0} 0 0\n'.format(surf_ref))
|
||||
|
||||
def parse_blender_face(self, bl_face, ac_mats):
|
||||
|
||||
if bl_face.material_index in ac_mats:
|
||||
self.mat = ac_mats[bl_face.material_index]
|
||||
|
||||
self.ac_surf_flags.smooth_shaded = bl_face.use_smooth
|
||||
self.ac_surf_flags.twosided = self.is_two_sided
|
||||
|
||||
class SurfaceFlags:
|
||||
def __init__( self,
|
||||
surf_type,
|
||||
is_smooth,
|
||||
is_twosided ):
|
||||
self.surf_type = surf_type
|
||||
self.smooth_shaded = is_smooth
|
||||
self.twosided = is_twosided
|
||||
|
||||
def getFlags(self):
|
||||
n = self.surf_type & 0x0f
|
||||
if self.smooth_shaded:
|
||||
n = n | 0x10
|
||||
if self.twosided:
|
||||
n = n | 0x20
|
||||
return n
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
class Group (Object):
|
||||
'''
|
||||
An object group
|
||||
|
||||
TODO maybe add an option to prevent exporting empty groups
|
||||
'''
|
||||
def __init__( self,
|
||||
name,
|
||||
bl_obj,
|
||||
export_config,
|
||||
local_transform = Matrix() ):
|
||||
Object.__init__(self, name, 'group', bl_obj, export_config, local_transform)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
class Material:
|
||||
'''
|
||||
Container class that defines the material properties of the .ac MATERIAL
|
||||
'''
|
||||
def __init__( self,
|
||||
name = 'DefaultWhite',
|
||||
bl_mat = None,
|
||||
export_config = None ):
|
||||
self.name = name # string
|
||||
self.rgb = [1.0, 1.0, 1.0] # [R,G,B]
|
||||
self.amb = [0.2, 0.2, 0.2] # [R,G,B]
|
||||
self.emis = [0.0, 0.0, 0.0] # [R,G,B]
|
||||
self.spec = [0.5, 0.5, 0.5] # [R,G,B]
|
||||
self.shi = 10 # integer
|
||||
self.trans = 0 # float
|
||||
|
||||
if bl_mat:
|
||||
self.name = bl_mat.name
|
||||
self.rgb = bl_mat.diffuse_color
|
||||
self.amb = [bl_mat.ambient, bl_mat.ambient, bl_mat.ambient]
|
||||
if export_config.mircol_as_emis:
|
||||
self.emis = bl_mat.mirror_color * bl_mat.emit
|
||||
else:
|
||||
self.emis = [bl_mat.emit, bl_mat.emit, bl_mat.emit]
|
||||
self.spec = bl_mat.specular_intensity * bl_mat.specular_color
|
||||
self.shi = bl_mat.specular_hardness
|
||||
if bl_mat.use_transparency:
|
||||
self.trans = 1.0 - bl_mat.alpha
|
||||
else:
|
||||
self.trans = 0.0
|
||||
|
||||
def write( self, strm ):
|
||||
# MATERIAL %s rgb %f %f %f amb %f %f %f emis %f %f %f spec %f %f %f shi %d trans %f
|
||||
|
||||
strm.write('MATERIAL "{0}" rgb {1:.4f} {2:.4f} {3:.4f} amb {4:.4f} {5:.4f} {6:.4f} emis {7:.4f} {8:.4f} {9:.4f} spec {10:.4f} {11:.4f} {12:.4f} shi {13} trans {14:.4f}\n'.format(
|
||||
self.name,
|
||||
self.rgb[0], self.rgb[1], self.rgb[2],
|
||||
self.amb[0], self.amb[1], self.amb[2],
|
||||
self.emis[0], self.emis[1], self.emis[2],
|
||||
self.spec[0], self.spec[1], self.spec[2],
|
||||
self.shi,
|
||||
self.trans,
|
||||
))
|
||||
|
||||
def same_as( self, rhs ):
|
||||
return self._feq(self.rgb[0], rhs.rgb[0]) and \
|
||||
self._feq(self.rgb[1], rhs.rgb[1]) and \
|
||||
self._feq(self.rgb[2], rhs.rgb[2]) and \
|
||||
self._feq(self.amb[0], rhs.amb[0]) and \
|
||||
self._feq(self.amb[1], rhs.amb[1]) and \
|
||||
self._feq(self.amb[2], rhs.amb[2]) and \
|
||||
self._feq(self.emis[0], rhs.emis[0]) and \
|
||||
self._feq(self.emis[1], rhs.emis[1]) and \
|
||||
self._feq(self.emis[2], rhs.emis[2]) and \
|
||||
self._feq(self.spec[0], rhs.spec[0]) and \
|
||||
self._feq(self.spec[1], rhs.spec[1]) and \
|
||||
self._feq(self.spec[2], rhs.spec[2]) and \
|
||||
self._feq(self.shi, rhs.shi) and \
|
||||
self._feq(self.trans, rhs.trans)
|
||||
|
||||
def _feq(self, lhs, rhs):
|
||||
return abs(rhs - lhs) < 0.0001
|
271
src/tools/blender/blender-2.6/io_scene_ac3d/__init__.py
Normal file
271
src/tools/blender/blender-2.6/io_scene_ac3d/__init__.py
Normal file
|
@ -0,0 +1,271 @@
|
|||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
# Most of this has been copied from the __init__.py file for the io_scene__xx
|
||||
# folders of the standard 2.59 blender package and customised to
|
||||
# act as a wrapper for the conventional AC3D importer/exporter
|
||||
|
||||
import time
|
||||
import datetime
|
||||
import bpy
|
||||
import mathutils
|
||||
from math import radians
|
||||
from bpy.props import StringProperty, BoolProperty, FloatProperty, EnumProperty
|
||||
from bpy_extras.io_utils import ImportHelper, ExportHelper, axis_conversion
|
||||
|
||||
bl_info = {
|
||||
"name": "AC3D (.ac) format",
|
||||
"description": "AC3D model exporter for blender.",
|
||||
"author": "Chris Marr",
|
||||
"version": (2,0),
|
||||
"blender" : (2,6,0),
|
||||
"api": 41098,
|
||||
"location": "File > Import-Export",
|
||||
"warning": "",
|
||||
"wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/Import-Export/Blender-AC3D",
|
||||
"tracker_url": "http://projects.blender.org/tracker/index.php?func=detail&aid=29007&group_id=153&atid=468",
|
||||
"category": "Import-Export"
|
||||
}
|
||||
|
||||
# To support reload properly, try to access a package var, if it's there,
|
||||
# reload everything
|
||||
if "bpy" in locals():
|
||||
import imp
|
||||
if 'import_ac3d' in locals():
|
||||
imp.reload(import_ac3d)
|
||||
if 'export_ac3d' in locals():
|
||||
imp.reload(export_ac3d)
|
||||
|
||||
def menu_func_import(self, context):
|
||||
self.layout.operator(ImportAC3D.bl_idname, text='AC3D (.ac)')
|
||||
|
||||
|
||||
def menu_func_export(self, context):
|
||||
self.layout.operator(ExportAC3D.bl_idname, text='AC3D (.ac)')
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_module(__name__)
|
||||
bpy.types.INFO_MT_file_import.append(menu_func_import)
|
||||
bpy.types.INFO_MT_file_export.append(menu_func_export)
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_module(__name__)
|
||||
bpy.types.INFO_MT_file_import.remove(menu_func_import)
|
||||
bpy.types.INFO_MT_file_export.remove(menu_func_export)
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
||||
|
||||
class ImportAC3D(bpy.types.Operator, ImportHelper):
|
||||
'''Import from AC3D file format (.ac)'''
|
||||
bl_idname = 'import_scene.import_ac3d'
|
||||
bl_label = 'Import AC3D'
|
||||
bl_options = {'PRESET'}
|
||||
|
||||
filename_ext = '.ac'
|
||||
filter_glob = StringProperty(default='*.ac', options={'HIDDEN'})
|
||||
|
||||
axis_forward = EnumProperty(
|
||||
name="Forward",
|
||||
items=(('X', "X Forward", ""),
|
||||
('Y', "Y Forward", ""),
|
||||
('Z', "Z Forward", ""),
|
||||
('-X', "-X Forward", ""),
|
||||
('-Y', "-Y Forward", ""),
|
||||
('-Z', "-Z Forward", ""),
|
||||
),
|
||||
default='-Z',
|
||||
)
|
||||
|
||||
axis_up = EnumProperty(
|
||||
name="Up",
|
||||
items=(('X', "X Up", ""),
|
||||
('Y', "Y Up", ""),
|
||||
('Z', "Z Up", ""),
|
||||
('-X', "-X Up", ""),
|
||||
('-Y', "-Y Up", ""),
|
||||
('-Z', "-Z Up", ""),
|
||||
),
|
||||
default='Y',
|
||||
)
|
||||
|
||||
use_transparency = BoolProperty(
|
||||
name="Use Transparency",
|
||||
description="Enable transparency for rendering if material alpha < 1.0",
|
||||
default=True,
|
||||
)
|
||||
|
||||
transparency_method = EnumProperty(
|
||||
name="Transparency Method",
|
||||
items=(('MASK', "Mask", ""),
|
||||
('Z_TRANSPARENCY', "Z_Transp", ""),
|
||||
('RAYTRACE', "RayTrace", ""),
|
||||
),
|
||||
default='Z_TRANSPARENCY',
|
||||
)
|
||||
|
||||
use_auto_smooth = BoolProperty(
|
||||
name="Auto Smooth",
|
||||
description="Use object auto smooth if normal angles are beneath Crease angle",
|
||||
default=True,
|
||||
)
|
||||
use_emis_as_mircol = BoolProperty(
|
||||
name="Use Emis as Mirror colour",
|
||||
description="Use Emission colour as Mirror colour",
|
||||
default=True,
|
||||
)
|
||||
|
||||
use_amb_as_mircol = BoolProperty(
|
||||
name="Use Amb as Mirror colour",
|
||||
description="Use Ambient colour as Mirror colour",
|
||||
default=False,
|
||||
)
|
||||
display_transparency = BoolProperty(
|
||||
name="Display Transparency",
|
||||
description="Display transparency in main display",
|
||||
default=True,
|
||||
)
|
||||
display_textured_solid = BoolProperty(
|
||||
name="Display textured solid",
|
||||
description="Show main window with textures applied (transparency works in only in normal direction)",
|
||||
default=False,
|
||||
)
|
||||
|
||||
def execute(self, context):
|
||||
from . import import_ac3d
|
||||
keywords = self.as_keywords(ignore=("axis_forward",
|
||||
"axis_up",
|
||||
"filter_glob",
|
||||
))
|
||||
|
||||
global_matrix = axis_conversion(from_forward=self.axis_forward,
|
||||
from_up=self.axis_up,
|
||||
).to_4x4()
|
||||
|
||||
keywords["global_matrix"] = global_matrix
|
||||
|
||||
t = time.mktime(datetime.datetime.now().timetuple())
|
||||
import_ac3d.ImportAC3D(self, context, **keywords)
|
||||
t = time.mktime(datetime.datetime.now().timetuple()) - t
|
||||
print('Finished importing in', t, 'seconds')
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
class ExportAC3D(bpy.types.Operator, ExportHelper):
|
||||
'''Export to AC3D file format (.ac)'''
|
||||
bl_idname = 'export_scene.export_ac3d'
|
||||
bl_label = 'Export AC3D'
|
||||
bl_options = {'PRESET'}
|
||||
|
||||
filename_ext = '.ac'
|
||||
|
||||
filter_glob = StringProperty(
|
||||
default='*.ac',
|
||||
options={'HIDDEN'}
|
||||
)
|
||||
|
||||
axis_forward = EnumProperty(
|
||||
name="Forward",
|
||||
items=(('X', "X Forward", ""),
|
||||
('Y', "Y Forward", ""),
|
||||
('Z', "Z Forward", ""),
|
||||
('-X', "-X Forward", ""),
|
||||
('-Y', "-Y Forward", ""),
|
||||
('-Z', "-Z Forward", ""),
|
||||
),
|
||||
default='-Z',
|
||||
)
|
||||
|
||||
axis_up = EnumProperty(
|
||||
name="Up",
|
||||
items=(('X', "X Up", ""),
|
||||
('Y', "Y Up", ""),
|
||||
('Z', "Z Up", ""),
|
||||
('-X', "-X Up", ""),
|
||||
('-Y', "-Y Up", ""),
|
||||
('-Z', "-Z Up", ""),
|
||||
),
|
||||
default='Y',
|
||||
)
|
||||
use_render_layers = BoolProperty(
|
||||
name="Only Render Layers",
|
||||
description="Only export from selected render layers",
|
||||
default=True,
|
||||
)
|
||||
use_selection = BoolProperty(
|
||||
name="Selection Only",
|
||||
description="Export selected objects only",
|
||||
default=False,
|
||||
)
|
||||
skip_data = BoolProperty(
|
||||
name="Skip Data",
|
||||
description="don't export mesh names as data fields",
|
||||
default=False,
|
||||
)
|
||||
global_coords = BoolProperty(
|
||||
name="Global Co-ordinates",
|
||||
description="Transform all vertices of all meshes to global coordinates",
|
||||
default=False,
|
||||
)
|
||||
mircol_as_emis = BoolProperty(
|
||||
name="Mirror col as Emis",
|
||||
description="export mirror colour as emissive colour",
|
||||
default=True,
|
||||
)
|
||||
mircol_as_amb = BoolProperty(
|
||||
name="Mirror col as Amb",
|
||||
description="export mirror colour as ambient colour",
|
||||
default=False,
|
||||
)
|
||||
crease_angle = FloatProperty(
|
||||
name="Default Crease Angle",
|
||||
description="Default crease angle for exported .ac faces",
|
||||
default=radians(179.0),
|
||||
options={"ANIMATABLE"},
|
||||
unit="ROTATION",
|
||||
subtype="ANGLE",
|
||||
)
|
||||
# This behaviour from the original exporter - not applicable?
|
||||
# no_split = BoolProperty(
|
||||
# name="No Split",
|
||||
# description="don't split meshes with multiple textures (or both textured and non-textured polygons)",
|
||||
# default=True,
|
||||
# )
|
||||
def execute(self, context):
|
||||
from . import export_ac3d
|
||||
keywords = self.as_keywords(ignore=("axis_forward",
|
||||
"axis_up",
|
||||
"filter_glob",
|
||||
"check_existing",
|
||||
))
|
||||
|
||||
global_matrix = axis_conversion(to_forward=self.axis_forward,
|
||||
to_up=self.axis_up,
|
||||
).to_4x4()
|
||||
|
||||
keywords["global_matrix"] = global_matrix
|
||||
t = time.mktime(datetime.datetime.now().timetuple())
|
||||
export_ac3d.ExportAC3D(self, context, **keywords)
|
||||
t = time.mktime(datetime.datetime.now().timetuple()) - t
|
||||
print('Finished exporting in', t, 'seconds')
|
||||
|
||||
return {'FINISHED'}
|
||||
|
182
src/tools/blender/blender-2.6/io_scene_ac3d/export_ac3d.py
Normal file
182
src/tools/blender/blender-2.6/io_scene_ac3d/export_ac3d.py
Normal file
|
@ -0,0 +1,182 @@
|
|||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
'''
|
||||
This file is a complete reboot of the AC3D export script that is used to export .ac format file into blender.
|
||||
|
||||
Reference to the .ac format is found here:
|
||||
http://www.inivis.com/ac3d/man/ac3dfileformat.html
|
||||
|
||||
Some noted points that are important for consideration:
|
||||
- AC3D appears to use Left Handed axes, but with Y oriented "Up". Blender uses Right Handed axes, the export does provide a rotation matrix applied to the world object that corrects this, so "Up" in the blender becomes "Up" in the .AC file - it's configurable, so you can change how it rotates...
|
||||
- AC3D supports only one texture per surface. This is a UV texture map, so only blenders texmap is exported
|
||||
- Blender's Materials can have multiple textures per material - so a material + texure in AC3D requires a distinct and unique material in blender. The export uses a comparison of material properties to see if a material is the same as another one and then uses that material index for the .ac file.
|
||||
|
||||
TODO: Option to define "DefaultWhite" material
|
||||
TODO: Optionally over-write existing textures
|
||||
'''
|
||||
|
||||
from . import AC3D
|
||||
|
||||
import os, bpy
|
||||
from math import radians
|
||||
from mathutils import Matrix
|
||||
|
||||
def TRACE(message):
|
||||
AC3D.TRACE(message)
|
||||
|
||||
class ExportConf:
|
||||
def __init__(
|
||||
self,
|
||||
operator,
|
||||
context,
|
||||
filepath,
|
||||
global_matrix,
|
||||
use_selection,
|
||||
use_render_layers,
|
||||
skip_data,
|
||||
global_coords,
|
||||
mircol_as_emis,
|
||||
mircol_as_amb,
|
||||
crease_angle,
|
||||
):
|
||||
# Stuff that needs to be available to the working classes (ha!)
|
||||
self.operator = operator
|
||||
self.context = context
|
||||
self.global_matrix = global_matrix
|
||||
self.use_selection = use_selection
|
||||
self.use_render_layers = use_render_layers
|
||||
self.skip_data = skip_data
|
||||
self.global_coords = global_coords
|
||||
self.mircol_as_emis = mircol_as_emis
|
||||
self.mircol_as_amb = mircol_as_amb
|
||||
self.crease_angle = crease_angle
|
||||
|
||||
# used to determine relative file paths
|
||||
self.exportdir = os.path.dirname(filepath)
|
||||
self.ac_name = os.path.split(filepath)[1]
|
||||
TRACE('Exporting to {0}'.format(self.ac_name))
|
||||
|
||||
class ExportAC3D:
|
||||
def __init__(
|
||||
self,
|
||||
operator,
|
||||
context,
|
||||
filepath='',
|
||||
global_matrix=None,
|
||||
use_selection=False,
|
||||
use_render_layers=True,
|
||||
skip_data=False,
|
||||
global_coords=False,
|
||||
mircol_as_emis=True,
|
||||
mircol_as_amb=False,
|
||||
crease_angle=radians(179.0),
|
||||
):
|
||||
|
||||
self.export_conf = ExportConf(
|
||||
operator,
|
||||
context,
|
||||
filepath,
|
||||
global_matrix,
|
||||
use_selection,
|
||||
use_render_layers,
|
||||
skip_data,
|
||||
global_coords,
|
||||
mircol_as_emis,
|
||||
mircol_as_amb,
|
||||
crease_angle,
|
||||
)
|
||||
|
||||
#TRACE("Global: {0}".format(global_matrix))
|
||||
|
||||
self.ac_mats = [AC3D.Material()]
|
||||
self.ac_world = None
|
||||
|
||||
# Parsing the tree in a top down manner and check on the way down which
|
||||
# objects are to be exported
|
||||
|
||||
self.world = AC3D.World('Blender_export__' + bpy.path.basename(filepath), self.export_conf)
|
||||
self.parseLevel(self.world, [ob for ob in bpy.data.objects if ob.parent == None and not ob.library])
|
||||
self.world.parse(self.ac_mats)
|
||||
|
||||
# dump the contents of the lists to file
|
||||
ac_file = open(filepath, 'w')
|
||||
ac_file.write('AC3Db\n')
|
||||
for ac_mat in self.ac_mats:
|
||||
ac_mat.write(ac_file)
|
||||
|
||||
#self.ac_world.write_ac_output(ac_file)
|
||||
self.world.write(ac_file)
|
||||
ac_file.close()
|
||||
|
||||
def parseLevel( self,
|
||||
parent,
|
||||
objects,
|
||||
ignore_select = False,
|
||||
local_transform = Matrix() ):
|
||||
'''
|
||||
Parse a level in the object hierarchy
|
||||
'''
|
||||
for ob in objects:
|
||||
|
||||
ac_ob = None
|
||||
|
||||
# Objects from libraries don't have the select flag set even if their
|
||||
# proxy is selected. We therefore consider all objects from libraries as
|
||||
# selected, as the only possibility to get them considered is if their
|
||||
# proxy should be exported.
|
||||
if (not self.export_conf.use_render_layers or ob.is_visible(self.export_conf.context.scene))\
|
||||
and (not self.export_conf.use_selection or ob.select or ignore_select):
|
||||
|
||||
# We need to check for dupligroups first as every type of object can be
|
||||
# converted to a dupligroup without removing the data from the old type.
|
||||
if ob.dupli_type == 'GROUP':
|
||||
ac_ob = AC3D.Group(ob.name, ob, self.export_conf, local_transform)
|
||||
children = [child for child in ob.dupli_group.objects
|
||||
if not child.parent
|
||||
or not child.parent.name in ob.dupli_group.objects]
|
||||
self.parseLevel(ac_ob, children, True, local_transform * ob.matrix_world)
|
||||
elif ob.type in ['MESH', 'LATTICE', 'SURFACE', 'CURVE']:
|
||||
ac_ob = AC3D.Poly(ob.name, ob, self.export_conf, local_transform)
|
||||
elif ob.type == 'ARMATURE':
|
||||
p = parent
|
||||
for bone in ob.pose.bones:
|
||||
for c in ob.children:
|
||||
if c.parent_bone == bone.name:
|
||||
ac_child = AC3D.Poly(c.name, c, self.export_conf, local_transform)
|
||||
p.addChild(ac_child)
|
||||
p = ac_child
|
||||
|
||||
if len(c.children):
|
||||
self.parseLevel(p, c.children, ignore_select, local_transform)
|
||||
continue
|
||||
# elif ob.type == 'EMPTY':
|
||||
# ac_ob = AC3D.Group(ob.name, ob, self.export_conf, local_transform)
|
||||
else:
|
||||
TRACE('Skipping object {0} (type={1})'.format(ob.name, ob.type))
|
||||
|
||||
if ac_ob:
|
||||
parent.addChild(ac_ob)
|
||||
next_parent = ac_ob
|
||||
else:
|
||||
# if link chain is broken (aka one element not exported) the object will
|
||||
# be placed in global space (=world)
|
||||
next_parent = self.world
|
||||
|
||||
if len(ob.children):
|
||||
self.parseLevel(next_parent, ob.children, ignore_select, local_transform)
|
693
src/tools/blender/blender-2.6/io_scene_ac3d/import_ac3d.py
Normal file
693
src/tools/blender/blender-2.6/io_scene_ac3d/import_ac3d.py
Normal file
|
@ -0,0 +1,693 @@
|
|||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
|
||||
import os
|
||||
import struct
|
||||
|
||||
import bpy
|
||||
import csv
|
||||
import mathutils
|
||||
from mathutils import Vector, Euler
|
||||
from math import radians
|
||||
from bpy import *
|
||||
from bpy_extras.image_utils import load_image
|
||||
from bpy_extras.io_utils import unpack_list, unpack_face_list
|
||||
|
||||
'''
|
||||
This file is a reboot of the AC3D import script that is used to import .ac format file into blender.
|
||||
|
||||
The original work carried out by Willian P Gerano was used to learn Python and Blender, but inherent issues and a desire to do better has prompted a complete re-write
|
||||
|
||||
Reference to the .ac format is found here:
|
||||
http://www.inivis.com/ac3d/man/ac3dfileformat.html
|
||||
|
||||
Some noted points that are important for consideration:
|
||||
- AC3D appears to use Left Handed axes, but with Y oriented "Up". Blender uses Right Handed axes, the import does provide a rotation matrix applied to the world object that corrects this, so "Up" in the AC file appears as "Up" in blender - it's configurable, so you can change how it rotates...
|
||||
- AC3D supports only one texture per surface. This is a UV texture map, so it's imported as such in blender
|
||||
- Blender's Materials can have multiple textures per material - so a material + texure in AC3D requires a distinct and unique material in blender (more of an issue for the export routine)
|
||||
- AC3D supports individual face twosidedness, blender's twosidedness is per-object
|
||||
TODO: Add setting so that .AC file is brought in relative to the 3D cursor
|
||||
|
||||
'''
|
||||
|
||||
DEBUG = True
|
||||
|
||||
def TRACE(message):
|
||||
if DEBUG:
|
||||
print(message)
|
||||
|
||||
class AcMat:
|
||||
'''
|
||||
Container class that defines the material properties of the .ac MATERIAL
|
||||
'''
|
||||
def __init__(self, name, rgb, amb, emis, spec, shi, trans, import_config):
|
||||
if name == "":
|
||||
name = "Default"
|
||||
self.name = name # string
|
||||
self.rgb = rgb # [R,G,B]
|
||||
self.amb = amb # [R,G,B]
|
||||
self.emis = emis # [R,G,B]
|
||||
self.spec = spec # [R,G,B]
|
||||
self.shi = shi # integer
|
||||
self.trans = trans # float
|
||||
|
||||
self.bmat_keys = {} # dictionary list of blender materials
|
||||
self.bmat_keys.setdefault(None)
|
||||
self.bl_material = None # untextured material
|
||||
self.import_config = import_config
|
||||
|
||||
def make_blender_mat(self, bl_mat):
|
||||
bl_mat.diffuse_color = self.rgb
|
||||
bl_mat.ambient = (self.amb[0] + self.amb[1] + self.amb[2]) / 3.0
|
||||
bl_mat.emit = (self.emis[0] + self.emis[1] + self.emis[2]) / 3.0
|
||||
bl_mat.specular_color = self.spec
|
||||
bl_mat.specular_intensity = float(self.shi) / 100
|
||||
bl_mat.alpha = 1.0 - self.trans
|
||||
if bl_mat.alpha < 1.0:
|
||||
bl_mat.use_transparency = self.import_config.use_transparency
|
||||
bl_mat.transparency_method = self.import_config.transparency_method
|
||||
|
||||
return bl_mat
|
||||
|
||||
'''
|
||||
looks for a matching blender material (optionally with a texture), adds it if it doesn't exist
|
||||
'''
|
||||
def get_blender_material(self, tex_name=''):
|
||||
bl_mat = None
|
||||
tex_slot = None
|
||||
if tex_name == '':
|
||||
bl_mat = self.bl_material
|
||||
if bl_mat == None:
|
||||
bl_mat = bpy.data.materials.new(self.name)
|
||||
bl_mat = self.make_blender_mat(bl_mat)
|
||||
|
||||
self.bl_material = bl_mat
|
||||
else:
|
||||
if tex_name in self.bmat_keys:
|
||||
bl_mat = self.bmat_keys[tex_name]
|
||||
else:
|
||||
bl_mat = bpy.data.materials.new(self.name)
|
||||
bl_mat = self.make_blender_mat(bl_mat)
|
||||
bl_mat.use_face_texture = True
|
||||
bl_mat.use_face_texture_alpha = True
|
||||
|
||||
tex_slot = bl_mat.texture_slots.add()
|
||||
tex_slot.texture = self.get_blender_texture(tex_name)
|
||||
tex_slot.texture_coords = 'UV'
|
||||
tex_slot.alpha_factor = 1.0
|
||||
tex_slot.use_map_alpha = True
|
||||
tex_slot.use = True
|
||||
tex_slot.uv_layer = tex_name
|
||||
self.bmat_keys[tex_name] = bl_mat
|
||||
return bl_mat
|
||||
|
||||
'''
|
||||
looks for the image in blender, adds it if it doesn't exist, returns the image to the callee
|
||||
'''
|
||||
def get_blender_image(self, tex_name):
|
||||
bl_image = None
|
||||
if tex_name in bpy.data.images:
|
||||
bl_image = bpy.data.images[tex_name]
|
||||
else:
|
||||
found = False
|
||||
base_name = bpy.path.basename(tex_name)
|
||||
for path in [ tex_name,
|
||||
os.path.join(self.import_config.importdir, tex_name),
|
||||
os.path.join(self.import_config.importdir, base_name) ]:
|
||||
if os.path.exists(path):
|
||||
found = True
|
||||
|
||||
try:
|
||||
bl_image = bpy.data.images.load(path)
|
||||
except:
|
||||
TRACE("Failed to load texture: {0}".format(tex_name))
|
||||
|
||||
if not found:
|
||||
TRACE("Failed to locate texture: {0}".format(tex_name))
|
||||
|
||||
return bl_image
|
||||
|
||||
'''
|
||||
looks for the blender texture, adds it if it doesn't exist
|
||||
'''
|
||||
def get_blender_texture(self, tex_name):
|
||||
bl_tex = None
|
||||
if tex_name in bpy.data.textures:
|
||||
bl_tex = bpy.data.textures[tex_name]
|
||||
else:
|
||||
bl_tex = bpy.data.textures.new(tex_name, 'IMAGE')
|
||||
bl_tex.image = self.get_blender_image(tex_name)
|
||||
bl_tex.use_preview_alpha = True
|
||||
|
||||
return bl_tex
|
||||
|
||||
class AcObj:
|
||||
'''
|
||||
Container class for a .ac OBJECT
|
||||
'''
|
||||
def __init__(self, ob_type, ac_file, import_config, parent = None):
|
||||
self.type = ob_type # Type of object
|
||||
self.ac_parent = parent # reference to the parent object (if the object is World, then this should be None)
|
||||
self.name = '' # name of the object
|
||||
self.data = '' # custom data
|
||||
self.tex_name = '' # texture name (filename of texture)
|
||||
self.texrep = [1,1] # texture repeat
|
||||
self.texoff = [0,0] # texture offset
|
||||
self.location = [0,0,0] # translation location of the center relative to the parent object
|
||||
self.rotation = mathutils.Matrix(([1,0,0],[0,1,0],[0,0,1])) # 3x3 rotational matrix for vertices
|
||||
self.url = '' # url of the object (??!)
|
||||
self.crease = 30 # crease angle for smoothing
|
||||
self.vert_list = [] # list of Vector(X,Y,Z) objects
|
||||
self.surf_list = [] # list of attached surfaces
|
||||
self.face_list = [] # flattened surface list
|
||||
self.edge_list = [] # spare edge list (handles poly lines etc)
|
||||
self.face_mat_list = [] # flattened surface material index list
|
||||
self.children = []
|
||||
self.bl_mat_dict = {} # Dictionary of ac_material index/texture pair to blender mesh material index
|
||||
|
||||
self.bl_obj = None # Blender object
|
||||
self.import_config = import_config
|
||||
|
||||
self.tokens = {
|
||||
'numvert': self.read_vertices,
|
||||
'numsurf': self.read_surfaces,
|
||||
'name': self.read_name,
|
||||
'data': self.read_data,
|
||||
'kids': self.read_children,
|
||||
'loc': self.read_location,
|
||||
'rot': self.read_rotation,
|
||||
'texture': self.read_texture,
|
||||
'texrep': self.read_texrep,
|
||||
'texoff': self.read_texoff,
|
||||
'subdiv': self.read_subdiv,
|
||||
'crease': self.read_crease
|
||||
}
|
||||
|
||||
self.read_ac_object(ac_file)
|
||||
|
||||
'''
|
||||
Read the object lines and dump them into this object, making hierarchial attachments to parents
|
||||
'''
|
||||
def read_ac_object(self, ac_file):
|
||||
bDone = False
|
||||
while not bDone:
|
||||
line = ac_file.readline()
|
||||
if line == '':
|
||||
break
|
||||
toks = line.strip().split()
|
||||
if len(toks)>0:
|
||||
if toks[0] in self.tokens.keys():
|
||||
bDone = self.tokens[toks[0]](ac_file,toks)
|
||||
else:
|
||||
bDone = True
|
||||
|
||||
def read_vertices(self, ac_file, toks):
|
||||
vertex_count = int(toks[1])
|
||||
for n in range(vertex_count):
|
||||
line = ac_file.readline()
|
||||
line = line.strip().split()
|
||||
self.vert_list.append(self.import_config.global_matrix * Vector([float(x) for x in line]))
|
||||
|
||||
def read_surfaces(self, ac_file, toks):
|
||||
surf_count = int(toks[1])
|
||||
|
||||
for n in range(surf_count):
|
||||
line = ac_file.readline()
|
||||
if line=='':
|
||||
break
|
||||
|
||||
line = line.strip().split()
|
||||
if line[0] == 'SURF':
|
||||
surf = AcSurf(line[1], ac_file, self.import_config)
|
||||
# TODO check this fix which just ignores everything but quads and triangles
|
||||
if( len(surf.refs) in [3,4] ):
|
||||
self.surf_list.append(surf)
|
||||
else:
|
||||
TRACE("Ignoring surface (vertex-count: {0})".format(len(surf.refs)))
|
||||
|
||||
def read_name(self, ac_file, toks):
|
||||
self.name=toks[1].strip('"')
|
||||
return False
|
||||
|
||||
def read_data(self, ac_file, toks):
|
||||
line = ac_file.readline()
|
||||
self.data=line[:int(toks[1])]
|
||||
return False
|
||||
|
||||
def read_location(self, ac_file, toks):
|
||||
self.location=(self.import_config.global_matrix * Vector([float(x) for x in toks[1:4]]))
|
||||
return False
|
||||
|
||||
def read_rotation(self, ac_file, toks):
|
||||
self.rotation = mathutils.Matrix(([float(x) for x in toks[1:4]], [float(x) for x in toks[4:7]], [float(x) for x in toks[7:10]]))
|
||||
# TODO check
|
||||
# rotation = mathutils.Matrix(( [float(x) for x in toks[1:4]],
|
||||
# [float(x) for x in toks[4:7]],
|
||||
# [float(x) for x in toks[7:10]] )).to_quaternion()
|
||||
# rotation.axis = self.import_config.global_matrix * rotation.axis
|
||||
# self.rotation = rotation.to_matrix()
|
||||
return False
|
||||
|
||||
def read_texture(self, ac_file, toks):
|
||||
self.tex_name=toks[1].strip('"')
|
||||
return False
|
||||
|
||||
def read_texrep(self, ac_file, toks):
|
||||
self.texrep=toks[1:2]
|
||||
return False
|
||||
|
||||
def read_texoff(self, ac_file, toks):
|
||||
self.texoff=toks[1:2]
|
||||
return False
|
||||
|
||||
def read_subdiv(self, ac_file, toks):
|
||||
self.subdiv=int(toks[1])
|
||||
return False
|
||||
|
||||
def read_crease(self, ac_file, toks):
|
||||
self.crease=float(toks[1])
|
||||
return False
|
||||
|
||||
def read_children(self, ac_file, toks):
|
||||
num_kids = int(toks[1])
|
||||
for n in range(num_kids):
|
||||
line = ac_file.readline()
|
||||
if line == '':
|
||||
break
|
||||
line = line.strip().split()
|
||||
self.children.append(AcObj(line[1].strip('"'), ac_file, self.import_config, self))
|
||||
# This is assumed to be the last thing in the list of things to read
|
||||
# returning True indicates to cease parsing this object
|
||||
return True
|
||||
|
||||
|
||||
'''
|
||||
This function does the work of creating an object in blender and configuring it correctly
|
||||
'''
|
||||
def create_blender_object(self, ac_matlist, str_pre, bLevelLinked):
|
||||
|
||||
if self.type == 'world':
|
||||
self.name = self.import_config.ac_name
|
||||
self.rotation = self.import_config.global_matrix
|
||||
me = None
|
||||
if self.type == 'group':
|
||||
# Create an empty object
|
||||
self.bl_obj = bpy.data.objects.new(self.name, None)
|
||||
|
||||
if self.type == 'poly':
|
||||
meshname = self.name
|
||||
if len(self.data)>0:
|
||||
meshname = self.data
|
||||
me = bpy.data.meshes.new(meshname)
|
||||
self.bl_obj = bpy.data.objects.new(self.name, me)
|
||||
|
||||
# setup parent object
|
||||
if self.ac_parent:
|
||||
self.bl_obj.parent = self.ac_parent.bl_obj
|
||||
|
||||
# make sure we have something to work with
|
||||
if self.vert_list and me:
|
||||
|
||||
me.use_auto_smooth = self.import_config.use_auto_smooth
|
||||
me.auto_smooth_angle = radians(self.crease)
|
||||
|
||||
for surf in self.surf_list:
|
||||
surf_edges = surf.get_edges()
|
||||
surf_face = surf.get_faces()
|
||||
for edge in surf_edges:
|
||||
self.edge_list.append(edge)
|
||||
|
||||
if surf.flags.type == 0:
|
||||
# test for invalid face (ie, >4 vertices)
|
||||
if len(surf.refs) > 4 or len(surf.refs) < 3:
|
||||
# not bringing in faces (assumed that there are none in a poly-line)
|
||||
TRACE("Ignoring surface (vertex-count: {0})".format(len(surf.refs)))
|
||||
else:
|
||||
|
||||
self.face_list.append(surf_face)
|
||||
|
||||
# Material index is 1 based, the list we built is 0 based
|
||||
ac_material = ac_matlist[surf.mat_index]
|
||||
bl_material = ac_material.get_blender_material(self.tex_name)
|
||||
|
||||
if bl_material == None:
|
||||
TRACE("Error getting material {0} '{1}'".format(surf.mat_index, self.tex_name))
|
||||
|
||||
fm_index = 0
|
||||
if not bl_material.name in me.materials:
|
||||
me.materials.append(bl_material)
|
||||
fm_index = len(me.materials)-1
|
||||
else:
|
||||
for mat in me.materials:
|
||||
if mat == bl_material:
|
||||
continue
|
||||
fm_index += 1
|
||||
if fm_index > len(me.materials):
|
||||
TRACE("Failed to find material index")
|
||||
fm_index = 0
|
||||
self.face_mat_list.append(fm_index)
|
||||
else:
|
||||
# treating as a polyline (nothing more to do)
|
||||
pass
|
||||
|
||||
me.vertices.add(len(self.vert_list))
|
||||
me.tessfaces.add(len(self.face_list))
|
||||
|
||||
# verts_loc is a list of (x, y, z) tuples
|
||||
me.vertices.foreach_set("co", unpack_list(self.vert_list))
|
||||
|
||||
# faces is a list of (vert_indices, texco_indices, ...) tuples
|
||||
me.tessfaces.foreach_set("vertices_raw", unpack_face_list(self.face_list))
|
||||
|
||||
# face_mat = [m for m in self.face_mat_list]
|
||||
# me.tessfaces.foreach_set("material_index", face_mat)
|
||||
# del face_mat
|
||||
|
||||
if len(self.tex_name):
|
||||
me.tessface_uv_textures.new()
|
||||
# uv_tex.active = True
|
||||
# uv_tex.active_render = True
|
||||
else:
|
||||
uv_tex = None
|
||||
|
||||
two_sided_lighting = False
|
||||
|
||||
for i, face in enumerate(self.face_list):
|
||||
blender_face = me.tessfaces[i]
|
||||
surf = self.surf_list[i]
|
||||
|
||||
blender_face.use_smooth = surf.flags.shaded
|
||||
|
||||
# If one surface is twosided, they all will be...
|
||||
two_sided_lighting |= surf.flags.two_sided
|
||||
|
||||
if len(self.tex_name) and len(surf.uv_refs) >= 3:
|
||||
blender_tface = me.tessface_uv_textures[0].data[i]
|
||||
|
||||
blender_tface.uv1 = surf.uv_refs[0]
|
||||
blender_tface.uv2 = surf.uv_refs[1]
|
||||
blender_tface.uv3 = surf.uv_refs[2]
|
||||
|
||||
if len(surf.uv_refs) > 3:
|
||||
blender_tface.uv4 = surf.uv_refs[3]
|
||||
|
||||
surf_material = me.materials[self.face_mat_list[i]]
|
||||
blender_tface.image = surf_material.texture_slots[0].texture.image
|
||||
|
||||
# uv_tex.data[f_index].use_image = True
|
||||
|
||||
me.show_double_sided = two_sided_lighting
|
||||
|
||||
self.bl_obj.show_transparent = self.import_config.display_transparency
|
||||
|
||||
if self.bl_obj:
|
||||
self.bl_obj.rotation_euler = self.rotation.to_euler()
|
||||
|
||||
self.bl_obj.location = self.location
|
||||
|
||||
self.import_config.context.scene.objects.link(self.bl_obj)
|
||||
# There's a bug somewhere - this ought to work....
|
||||
self.import_config.context.scene.objects.active = self.bl_obj
|
||||
# bpy.ops.object.origin_set('ORIGIN_GEOMETRY', 'MEDIAN')
|
||||
|
||||
|
||||
TRACE("{0}+-{1} ({2})".format(str_pre, self.name, self.data))
|
||||
|
||||
# Add any children
|
||||
str_pre_new = ""
|
||||
bUseLink = True
|
||||
for obj in self.children:
|
||||
if bLevelLinked:
|
||||
str_pre_new = str_pre + "| "
|
||||
else:
|
||||
str_pre_new = str_pre + " "
|
||||
|
||||
if self.children.index(obj) == len(self.children)-1:
|
||||
bUseLink = False
|
||||
|
||||
obj.create_blender_object(ac_matlist, str_pre_new, bUseLink)
|
||||
|
||||
|
||||
if me:
|
||||
# me.calc_normals()
|
||||
me.validate()
|
||||
me.update(calc_edges=True)
|
||||
|
||||
|
||||
class AcSurf:
|
||||
class AcSurfFlags:
|
||||
def __init__(self, flags):
|
||||
self.type = 0 # Surface Type: 0=Polygon, 1=closedLine, 2=Line
|
||||
self.shaded = False
|
||||
self.two_sided = False
|
||||
i = int(flags,16)
|
||||
|
||||
self.type = i & 0xF
|
||||
i = i >> 4
|
||||
if i&1:
|
||||
self.shaded = True
|
||||
if i&2:
|
||||
self.two_sided = True
|
||||
|
||||
'''
|
||||
Container class for surface definition within a parent object
|
||||
'''
|
||||
def __init__(self, flags, ac_file, import_config):
|
||||
self.flags = self.AcSurfFlags(flags) # surface flags
|
||||
self.mat_index = 0 # default material
|
||||
self.refs = [] # list of indexes into the parent objects defined vertexes with defined UV coordinates
|
||||
self.uv_refs = []
|
||||
self.tokens = {
|
||||
'mat': self.read_surf_material,
|
||||
'refs': self.read_surf_refs,
|
||||
}
|
||||
|
||||
self.import_config = import_config
|
||||
self.read_ac_surfaces(ac_file)
|
||||
|
||||
def read_ac_surfaces(self, ac_file):
|
||||
surf_done=False
|
||||
while not surf_done:
|
||||
line = ac_file.readline()
|
||||
if line=='':
|
||||
break
|
||||
toks = line.split()
|
||||
if len(toks)>0:
|
||||
if toks[0] in self.tokens.keys():
|
||||
surf_done = self.tokens[toks[0]](ac_file,toks)
|
||||
else:
|
||||
surf_done = True
|
||||
|
||||
def read_surf_material(self, ac_file, tokens):
|
||||
self.mat_index = int(tokens[1])
|
||||
return False
|
||||
|
||||
def read_surf_refs(self, ac_file, tokens):
|
||||
num_refs = int(tokens[1])
|
||||
for n in range(num_refs):
|
||||
line = ac_file.readline()
|
||||
line = line.strip().split()
|
||||
|
||||
self.refs.append(int(line[0]))
|
||||
self.uv_refs.append([float(x) for x in line[1:3]])
|
||||
return True
|
||||
|
||||
def get_faces(self):
|
||||
# convert refs and surface type to faces
|
||||
surf_faces = []
|
||||
# make sure it's a face type polygon and that there's the right number of vertices
|
||||
if self.flags.type == 0 and len(self.refs) in [3,4]:
|
||||
surf_faces = self.refs
|
||||
return surf_faces
|
||||
|
||||
def get_edges(self):
|
||||
# convert refs and surface type to edges
|
||||
surf_edges = []
|
||||
if self.flags.type != 0:
|
||||
# poly-line
|
||||
for x in range(len(self.refs)-1):
|
||||
surf_edges.append([self.refs[x],self.refs[x+1]])
|
||||
|
||||
if self.flags.type == 1:
|
||||
# closed poly-line
|
||||
surf_edges.append([self.refs[len(self.refs)-1],self.refs[0]])
|
||||
return surf_edges
|
||||
|
||||
class ImportConf:
|
||||
def __init__(
|
||||
self,
|
||||
operator,
|
||||
context,
|
||||
filepath,
|
||||
global_matrix,
|
||||
use_transparency,
|
||||
transparency_method,
|
||||
use_auto_smooth,
|
||||
use_emis_as_mircol,
|
||||
use_amb_as_mircol,
|
||||
display_transparency,
|
||||
display_textured_solid,
|
||||
):
|
||||
# Stuff that needs to be available to the working classes (ha!)
|
||||
self.operator = operator
|
||||
self.context = context
|
||||
self.global_matrix = global_matrix
|
||||
self.use_transparency = use_transparency
|
||||
self.transparency_method = transparency_method
|
||||
self.use_auto_smooth = use_auto_smooth
|
||||
self.use_emis_as_mircol = use_emis_as_mircol
|
||||
self.use_amb_as_mircol = use_amb_as_mircol
|
||||
self.display_transparency = display_transparency
|
||||
self.display_textured_solid = display_textured_solid
|
||||
|
||||
# used to determine relative file paths
|
||||
self.importdir = os.path.dirname(filepath)
|
||||
self.ac_name = os.path.split(filepath)[1]
|
||||
TRACE("Importing {0}".format(self.ac_name))
|
||||
|
||||
class ImportAC3D:
|
||||
def __init__(
|
||||
self,
|
||||
operator,
|
||||
context,
|
||||
filepath="",
|
||||
use_image_search=False,
|
||||
global_matrix=None,
|
||||
use_transparency=True,
|
||||
transparency_method='Z_TRANSPARENCY',
|
||||
use_auto_smooth=True,
|
||||
use_emis_as_mircol=True,
|
||||
use_amb_as_mircol=False,
|
||||
display_transparency=True,
|
||||
display_textured_solid=False,
|
||||
):
|
||||
|
||||
self.import_config = ImportConf(
|
||||
operator,
|
||||
context,
|
||||
filepath,
|
||||
global_matrix,
|
||||
use_transparency,
|
||||
transparency_method,
|
||||
use_auto_smooth,
|
||||
use_emis_as_mircol,
|
||||
use_amb_as_mircol,
|
||||
display_transparency,
|
||||
display_textured_solid,
|
||||
)
|
||||
|
||||
|
||||
self.tokens = {
|
||||
'MATERIAL': self.read_material,
|
||||
'OBJECT': self.read_object,
|
||||
}
|
||||
self.oblist = []
|
||||
self.matlist = []
|
||||
|
||||
operator.report({'INFO'}, "Attempting import: {file}".format(file=filepath))
|
||||
|
||||
# Check to make sure we're working with a valid AC3D file
|
||||
ac_file = open(filepath, 'r')
|
||||
self.header = ac_file.readline().strip()
|
||||
if len(self.header) != 5:
|
||||
operator.report({'ERROR'},"Invalid file header length: {0}".format(self.header))
|
||||
ac_file.close()
|
||||
return None
|
||||
|
||||
#pull out the AC3D file header
|
||||
AC3D_header = self.header[:4]
|
||||
AC3D_ver = self.header[4:5]
|
||||
if AC3D_header != 'AC3D':
|
||||
operator.report({'ERROR'},"Invalid file header: {0}".format(self.header))
|
||||
ac_file.close()
|
||||
return None
|
||||
|
||||
self.read_ac_file(ac_file)
|
||||
|
||||
ac_file.close()
|
||||
|
||||
self.create_blender_data()
|
||||
|
||||
# Display as either textured solid (transparency only works in one direction) or as textureless solids (transparency works)
|
||||
for bl_screen in bpy.data.screens:
|
||||
for bl_area in bl_screen.areas:
|
||||
for bl_space in bl_area.spaces:
|
||||
if bl_space.type == 'VIEW_3D':
|
||||
bl_space.show_textured_solid = self.import_config.display_textured_solid
|
||||
|
||||
|
||||
return None
|
||||
|
||||
'''
|
||||
Simplifies the reporting of errors to the user
|
||||
'''
|
||||
def report_error(self, message):
|
||||
TRACE(message)
|
||||
self.import_config.operator.report({'ERROR'},message)
|
||||
|
||||
'''
|
||||
read our validated .ac file
|
||||
'''
|
||||
def read_ac_file(self, ac_file):
|
||||
reader = csv.reader(ac_file, delimiter=' ', skipinitialspace=True)
|
||||
try:
|
||||
for row in reader:
|
||||
# See if this is a valid token and pass the file handle and the current line to our function
|
||||
if row[0] in self.tokens.keys():
|
||||
self.tokens[row[0]](ac_file,row)
|
||||
else:
|
||||
self.report_error("invalid token: {tok} ({ln})".format(tok=row[0], ln=row))
|
||||
except csv.Error(e):
|
||||
self.report_error('AC3D import error, line %d: %s' % (reader.line_num, e))
|
||||
|
||||
'''
|
||||
Take the passed in line and interpret as a .ac material
|
||||
'''
|
||||
def read_material(self, ac_file, line):
|
||||
|
||||
# MATERIAL %s rgb %f %f %f amb %f %f %f emis %f %f %f spec %f %f %f shi %d trans %f
|
||||
self.matlist.append(AcMat(line[1],
|
||||
[float(x) for x in line[3:6]],
|
||||
[float(x) for x in line[7:10]],
|
||||
[float(x) for x in line[11:14]],
|
||||
[float(x) for x in line[15:18]],
|
||||
float(line[19]), # it should be int but float seems to be used sometimes
|
||||
float(line[21]),
|
||||
self.import_config,
|
||||
))
|
||||
|
||||
'''
|
||||
Read the Object definition (including child objects)
|
||||
'''
|
||||
def read_object(self, ac_file, line):
|
||||
# OBJECT %s
|
||||
self.oblist.append(AcObj(line[1], ac_file, self.import_config))
|
||||
|
||||
'''
|
||||
Reads the data imported from the file and creates blender data
|
||||
'''
|
||||
def create_blender_data(self):
|
||||
|
||||
# go through the list of objects
|
||||
bUseLink = True
|
||||
for obj in self.oblist:
|
||||
if self.oblist.index(obj) == len(self.oblist)-1:
|
||||
bUseLink = False
|
||||
obj.create_blender_object(self.matlist, "", bUseLink)
|
||||
|
Loading…
Reference in a new issue