forked from bartvdbraak/blender
34a617e308
There is a problem importing 3ds files where I cant find a way to check if the transforms are applied to the vertex locations or not. Since 2.44 I made the importer assume they were not since you can manually remove transformations, but not reverse. Nevertheless most 3ds files have the matrix applied, better not give a bad import by default. Did some research and other 3ds importers (lib3ds for eg), have the same problem and just assume the transformations applied. 3dsMax imports both correctly so there must be a way to tell but I could not link it to the 3ds version or other mesh options. Added an option to workaround this problem in rare cases where its needed. - KX_GameObject.cpp & KX_Scene.cpp, clear the dict before removing the reference in case there is a circular reference.
1002 lines
33 KiB
Python
1002 lines
33 KiB
Python
#!BPY
|
|
"""
|
|
Name: '3D Studio (.3ds)...'
|
|
Blender: 244
|
|
Group: 'Import'
|
|
Tooltip: 'Import from 3DS file format (.3ds)'
|
|
"""
|
|
|
|
__author__= ['Bob Holcomb', 'Richard L?rk?ng', 'Damien McGinnes', 'Campbell Barton', 'Mario Lapin']
|
|
__url__ = ("blenderartists.org", "www.blender.org", "www.gametutorials.com", "lib3ds.sourceforge.net/")
|
|
__version__= '0.996'
|
|
__bpydoc__= '''\
|
|
|
|
3ds Importer
|
|
|
|
This script imports a 3ds file and the materials into Blender for editing.
|
|
|
|
Loader is based on 3ds loader from www.gametutorials.com (Thanks DigiBen).
|
|
|
|
0.996 by Mario Lapin (mario.lapin@gmail.com) 13/04/200 <br>
|
|
- Implemented workaround to correct association between name, geometry and materials of
|
|
imported meshes.
|
|
|
|
Without this patch, version 0.995 of this importer would associate to each mesh object the
|
|
geometry and the materials of the previously parsed mesh object. By so, the name of the
|
|
first mesh object would be thrown away, and the name of the last mesh object would be
|
|
automatically merged with a '.001' at the end. No object would desappear, however object's
|
|
names and materials would be completely jumbled.
|
|
|
|
0.995 by Campbell Barton<br>
|
|
- workaround for buggy mesh vert delete
|
|
- minor tweaks
|
|
|
|
0.99 by Bob Holcomb<br>
|
|
- added support for floating point color values that previously broke on import.
|
|
|
|
0.98 by Campbell Barton<br>
|
|
- import faces and verts to lists instead of a mesh, convert to a mesh later
|
|
- use new index mapping feature of mesh to re-map faces that were not added.
|
|
|
|
0.97 by Campbell Barton<br>
|
|
- Strip material names of spaces
|
|
- Added import as instance to import the 3ds into its own
|
|
scene and add a group instance to the current scene
|
|
- New option to scale down imported objects so they are within a limited bounding area.
|
|
|
|
0.96 by Campbell Barton<br>
|
|
- Added workaround for bug in setting UV's for Zero vert index UV faces.
|
|
- Removed unique name function, let blender make the names unique.
|
|
|
|
0.95 by Campbell Barton<br>
|
|
- Removed workarounds for Blender 2.41
|
|
- Mesh objects split by material- many 3ds objects used more then 16 per mesh.
|
|
- Removed a lot of unneeded variable creation.
|
|
|
|
0.94 by Campbell Barton<br>
|
|
- Face import tested to be about overall 16x speedup over 0.93.
|
|
- Material importing speedup.
|
|
- Tested with more models.
|
|
- Support some corrupt models.
|
|
|
|
0.93 by Campbell Barton<br>
|
|
- Tested with 400 3ds files from turbosquid and samples.
|
|
- Tactfully ignore faces that used the same verts twice.
|
|
- Rollback to 0.83 sloppy un-reorganized code, this broke UV coord loading.
|
|
- Converted from NMesh to Mesh.
|
|
- Faster and cleaner new names.
|
|
- Use external comprehensive image loader.
|
|
- Re intergrated 0.92 and 0.9 changes
|
|
- Fixes for 2.41 compat.
|
|
- Non textured faces do not use a texture flag.
|
|
|
|
0.92<br>
|
|
- Added support for diffuse, alpha, spec, bump maps in a single material
|
|
|
|
0.9<br>
|
|
- Reorganized code into object/material block functions<br>
|
|
- Use of Matrix() to copy matrix data<br>
|
|
- added support for material transparency<br>
|
|
|
|
0.83 2005-08-07: Campell Barton
|
|
- Aggressive image finding and case insensitivy for posisx systems.
|
|
|
|
0.82a 2005-07-22
|
|
- image texture loading (both for face uv and renderer)
|
|
|
|
0.82 - image texture loading (for face uv)
|
|
|
|
0.81a (fork- not 0.9) Campbell Barton 2005-06-08
|
|
- Simplified import code
|
|
- Never overwrite data
|
|
- Faster list handling
|
|
- Leaves import selected
|
|
|
|
0.81 Damien McGinnes 2005-01-09
|
|
- handle missing images better
|
|
|
|
0.8 Damien McGinnes 2005-01-08
|
|
- copies sticky UV coords to face ones
|
|
- handles images better
|
|
- Recommend that you run 'RemoveDoubles' on each imported mesh after using this script
|
|
|
|
'''
|
|
|
|
# ***** BEGIN GPL LICENSE BLOCK *****
|
|
#
|
|
# Script copyright (C) Bob Holcomb
|
|
#
|
|
# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
#
|
|
# ***** END GPL LICENCE BLOCK *****
|
|
# --------------------------------------------------------------------------
|
|
|
|
# Importing modules
|
|
|
|
import Blender
|
|
import bpy
|
|
from Blender import Mesh, Object, Material, Image, Texture, Lamp, Mathutils
|
|
from Blender.Mathutils import Vector
|
|
import BPyImage
|
|
|
|
import BPyMessages
|
|
|
|
import struct
|
|
from struct import calcsize, unpack
|
|
|
|
import os
|
|
|
|
# If python version is less than 2.4, try to get set stuff from module
|
|
try:
|
|
set
|
|
except:
|
|
from sets import Set as set
|
|
|
|
BOUNDS_3DS= []
|
|
|
|
|
|
#this script imports uvcoords as sticky vertex coords
|
|
#this parameter enables copying these to face uv coords
|
|
#which shold be more useful.
|
|
|
|
def createBlenderTexture(material, name, image):
|
|
texture= bpy.data.textures.new(name)
|
|
texture.setType('Image')
|
|
texture.image= image
|
|
material.setTexture(0, texture, Texture.TexCo.UV, Texture.MapTo.COL)
|
|
|
|
|
|
|
|
######################################################
|
|
# Data Structures
|
|
######################################################
|
|
|
|
#Some of the chunks that we will see
|
|
#----- Primary Chunk, at the beginning of each file
|
|
PRIMARY= long('0x4D4D',16)
|
|
|
|
#------ Main Chunks
|
|
OBJECTINFO = long('0x3D3D',16); #This gives the version of the mesh and is found right before the material and object information
|
|
VERSION = long('0x0002',16); #This gives the version of the .3ds file
|
|
EDITKEYFRAME= long('0xB000',16); #This is the header for all of the key frame info
|
|
|
|
#------ sub defines of OBJECTINFO
|
|
MATERIAL=45055 #0xAFFF // This stored the texture info
|
|
OBJECT=16384 #0x4000 // This stores the faces, vertices, etc...
|
|
|
|
#>------ sub defines of MATERIAL
|
|
#------ sub defines of MATERIAL_BLOCK
|
|
MAT_NAME = long('0xA000',16) # This holds the material name
|
|
MAT_AMBIENT = long('0xA010',16) # Ambient color of the object/material
|
|
MAT_DIFFUSE = long('0xA020',16) # This holds the color of the object/material
|
|
MAT_SPECULAR = long('0xA030',16) # SPecular color of the object/material
|
|
MAT_SHINESS = long('0xA040',16) # ??
|
|
MAT_TRANSPARENCY= long('0xA050',16) # Transparency value of material
|
|
MAT_SELF_ILLUM = long('0xA080',16) # Self Illumination value of material
|
|
MAT_WIRE = long('0xA085',16) # Only render's wireframe
|
|
|
|
MAT_TEXTURE_MAP = long('0xA200',16) # This is a header for a new texture map
|
|
MAT_SPECULAR_MAP= long('0xA204',16) # This is a header for a new specular map
|
|
MAT_OPACITY_MAP = long('0xA210',16) # This is a header for a new opacity map
|
|
MAT_REFLECTION_MAP= long('0xA220',16) # This is a header for a new reflection map
|
|
MAT_BUMP_MAP = long('0xA230',16) # This is a header for a new bump map
|
|
MAT_MAP_FILENAME = long('0xA300',16) # This holds the file name of the texture
|
|
|
|
MAT_FLOAT_COLOR = long ('0x0010', 16) #color defined as 3 floats
|
|
MAT_24BIT_COLOR = long ('0x0011', 16) #color defined as 3 bytes
|
|
|
|
#>------ sub defines of OBJECT
|
|
OBJECT_MESH = long('0x4100',16); # This lets us know that we are reading a new object
|
|
OBJECT_LAMP = long('0x4600',16); # This lets un know we are reading a light object
|
|
OBJECT_LAMP_SPOT = long('0x4610',16); # The light is a spotloght.
|
|
OBJECT_LAMP_OFF = long('0x4620',16); # The light off.
|
|
OBJECT_LAMP_ATTENUATE = long('0x4625',16);
|
|
OBJECT_LAMP_RAYSHADE = long('0x4627',16);
|
|
OBJECT_LAMP_SHADOWED = long('0x4630',16);
|
|
OBJECT_LAMP_LOCAL_SHADOW = long('0x4640',16);
|
|
OBJECT_LAMP_LOCAL_SHADOW2 = long('0x4641',16);
|
|
OBJECT_LAMP_SEE_CONE = long('0x4650',16);
|
|
OBJECT_LAMP_SPOT_RECTANGULAR= long('0x4651',16);
|
|
OBJECT_LAMP_SPOT_OVERSHOOT= long('0x4652',16);
|
|
OBJECT_LAMP_SPOT_PROJECTOR= long('0x4653',16);
|
|
OBJECT_LAMP_EXCLUDE= long('0x4654',16);
|
|
OBJECT_LAMP_RANGE= long('0x4655',16);
|
|
OBJECT_LAMP_ROLL= long('0x4656',16);
|
|
OBJECT_LAMP_SPOT_ASPECT= long('0x4657',16);
|
|
OBJECT_LAMP_RAY_BIAS= long('0x4658',16);
|
|
OBJECT_LAMP_INNER_RANGE= long('0x4659',16);
|
|
OBJECT_LAMP_OUTER_RANGE= long('0x465A',16);
|
|
OBJECT_LAMP_MULTIPLIER = long('0x465B',16);
|
|
OBJECT_LAMP_AMBIENT_LIGHT = long('0x4680',16);
|
|
|
|
|
|
|
|
OBJECT_CAMERA= long('0x4700',16); # This lets un know we are reading a camera object
|
|
|
|
#>------ sub defines of CAMERA
|
|
OBJECT_CAM_RANGES= long('0x4720',16); # The camera range values
|
|
|
|
#>------ sub defines of OBJECT_MESH
|
|
OBJECT_VERTICES = long('0x4110',16); # The objects vertices
|
|
OBJECT_FACES = long('0x4120',16); # The objects faces
|
|
OBJECT_MATERIAL = long('0x4130',16); # This is found if the object has a material, either texture map or color
|
|
OBJECT_UV = long('0x4140',16); # The UV texture coordinates
|
|
OBJECT_TRANS_MATRIX = long('0x4160',16); # The Object Matrix
|
|
|
|
global scn
|
|
scn= None
|
|
|
|
#the chunk class
|
|
class chunk:
|
|
ID=0
|
|
length=0
|
|
bytes_read=0
|
|
|
|
#we don't read in the bytes_read, we compute that
|
|
binary_format='<HI'
|
|
|
|
def __init__(self):
|
|
self.ID=0
|
|
self.length=0
|
|
self.bytes_read=0
|
|
|
|
def dump(self):
|
|
print 'ID: ', self.ID
|
|
print 'ID in hex: ', hex(self.ID)
|
|
print 'length: ', self.length
|
|
print 'bytes_read: ', self.bytes_read
|
|
|
|
def read_chunk(file, chunk):
|
|
temp_data=file.read(calcsize(chunk.binary_format))
|
|
data=unpack(chunk.binary_format, temp_data)
|
|
chunk.ID=data[0]
|
|
chunk.length=data[1]
|
|
#update the bytes read function
|
|
chunk.bytes_read=6
|
|
|
|
#if debugging
|
|
#chunk.dump()
|
|
|
|
def read_string(file):
|
|
#read in the characters till we get a null character
|
|
s=''
|
|
while not s.endswith('\x00'):
|
|
s+=unpack( '<c', file.read(1) )[0]
|
|
#print 'string: ',s
|
|
|
|
#remove the null character from the string
|
|
return s[:-1]
|
|
|
|
######################################################
|
|
# IMPORT
|
|
######################################################
|
|
def process_next_object_chunk(file, previous_chunk):
|
|
new_chunk=chunk()
|
|
temp_chunk=chunk()
|
|
|
|
while (previous_chunk.bytes_read<previous_chunk.length):
|
|
#read the next chunk
|
|
read_chunk(file, new_chunk)
|
|
|
|
def skip_to_end(file, skip_chunk):
|
|
buffer_size=skip_chunk.length-skip_chunk.bytes_read
|
|
binary_format='%ic' % buffer_size
|
|
temp_data=file.read(calcsize(binary_format))
|
|
skip_chunk.bytes_read+=buffer_size
|
|
|
|
|
|
def add_texture_to_material(image, texture, material, mapto):
|
|
if mapto=='DIFFUSE':
|
|
map=Texture.MapTo.COL
|
|
elif mapto=='SPECULAR':
|
|
map=Texture.MapTo.SPEC
|
|
elif mapto=='OPACITY':
|
|
map=Texture.MapTo.ALPHA
|
|
elif mapto=='BUMP':
|
|
map=Texture.MapTo.NOR
|
|
else:
|
|
print '/tError: Cannot map to "%s"\n\tassuming diffuse color. modify material "%s" later.' % (mapto, material.name)
|
|
map=Texture.MapTo.COL
|
|
|
|
if image: texture.setImage(image) # double check its an image.
|
|
free_tex_slots= [i for i, tex in enumerate( material.getTextures() ) if tex==None]
|
|
if not free_tex_slots:
|
|
print '/tError: Cannot add "%s" map. 10 Texture slots alredy used.' % mapto
|
|
else:
|
|
material.setTexture(free_tex_slots[0],texture,Texture.TexCo.UV,map)
|
|
|
|
|
|
def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH):
|
|
#print previous_chunk.bytes_read, 'BYTES READ'
|
|
contextObName= None
|
|
contextLamp= [None, None] # object, Data
|
|
contextMaterial= None
|
|
contextMatrix_rot= None # Blender.Mathutils.Matrix(); contextMatrix.identity()
|
|
#contextMatrix_tx= None # Blender.Mathutils.Matrix(); contextMatrix.identity()
|
|
contextMesh_vertls= None
|
|
contextMesh_facels= None
|
|
contextMeshMaterials= {} # matname:[face_idxs]
|
|
contextMeshUV= None
|
|
|
|
TEXTURE_DICT={}
|
|
MATDICT={}
|
|
TEXMODE= Mesh.FaceModes['TEX']
|
|
|
|
# Localspace variable names, faster.
|
|
STRUCT_SIZE_1CHAR= calcsize('c')
|
|
STRUCT_SIZE_2FLOAT= calcsize('2f')
|
|
STRUCT_SIZE_3FLOAT= calcsize('3f')
|
|
STRUCT_SIZE_UNSIGNED_SHORT= calcsize('H')
|
|
STRUCT_SIZE_4UNSIGNED_SHORT= calcsize('4H')
|
|
STRUCT_SIZE_4x3MAT= calcsize('ffffffffffff')
|
|
_STRUCT_SIZE_4x3MAT= calcsize('fffffffffffff')
|
|
# STRUCT_SIZE_4x3MAT= calcsize('ffffffffffff')
|
|
# print STRUCT_SIZE_4x3MAT, ' STRUCT_SIZE_4x3MAT'
|
|
|
|
def putContextMesh(myContextMesh_vertls, myContextMesh_facels, myContextMeshMaterials):
|
|
|
|
materialFaces= set() # faces that have a material. Can optimize?
|
|
|
|
# Now make copies with assigned materils.
|
|
|
|
def makeMeshMaterialCopy(matName, faces):
|
|
'''
|
|
Make a new mesh with only face the faces that use this material.
|
|
faces can be any iterable object - containing ints.
|
|
'''
|
|
|
|
faceVertUsers = [False] * len(myContextMesh_vertls)
|
|
ok=0
|
|
for fIdx in faces:
|
|
for vindex in myContextMesh_facels[fIdx]:
|
|
faceVertUsers[vindex] = True
|
|
if matName != None: # if matName is none then this is a set(), meaning we are using the untextured faces and do not need to store textured faces.
|
|
materialFaces.add(fIdx)
|
|
ok=1
|
|
|
|
if not ok:
|
|
return
|
|
|
|
myVertMapping = {}
|
|
vertMappingIndex = 0
|
|
|
|
vertsToUse = [i for i in xrange(len(myContextMesh_vertls)) if faceVertUsers[i]]
|
|
myVertMapping = dict( [ (ii, i) for i, ii in enumerate(vertsToUse) ] )
|
|
|
|
tempName= '%s_%s' % (contextObName, matName) # matName may be None.
|
|
bmesh = bpy.data.meshes.new(tempName)
|
|
|
|
if matName == None:
|
|
img= None
|
|
else:
|
|
bmat = MATDICT[matName][1]
|
|
bmesh.materials= [bmat]
|
|
try: img= TEXTURE_DICT[bmat.name]
|
|
except: img= None
|
|
|
|
bmesh_verts = bmesh.verts
|
|
bmesh_verts.extend( [Vector()] )
|
|
bmesh_verts.extend( [myContextMesh_vertls[i] for i in vertsToUse] )
|
|
# +1 because of DUMMYVERT
|
|
face_mapping= bmesh.faces.extend( [ [ bmesh_verts[ myVertMapping[vindex]+1] for vindex in myContextMesh_facels[fIdx]] for fIdx in faces ], indexList=True )
|
|
|
|
if bmesh.faces and (contextMeshUV or img):
|
|
bmesh.faceUV= 1
|
|
for ii, i in enumerate(faces):
|
|
|
|
# Mapped index- faces may have not been added- if so, then map to the correct index
|
|
# BUGGY API - face_mapping is not always the right length
|
|
map_index= face_mapping[ii]
|
|
|
|
if map_index != None:
|
|
targetFace= bmesh.faces[map_index]
|
|
if contextMeshUV:
|
|
# v.index-1 because of the DUMMYVERT
|
|
targetFace.uv= [contextMeshUV[vindex] for vindex in myContextMesh_facels[i]]
|
|
if img:
|
|
targetFace.image= img
|
|
|
|
# bmesh.transform(contextMatrix)
|
|
ob = SCN_OBJECTS.new(bmesh, tempName)
|
|
'''
|
|
if contextMatrix_tx:
|
|
ob.setMatrix(contextMatrix_tx)
|
|
'''
|
|
|
|
if contextMatrix_rot:
|
|
ob.setMatrix(contextMatrix_rot)
|
|
|
|
importedObjects.append(ob)
|
|
bmesh.calcNormals()
|
|
|
|
for matName, faces in myContextMeshMaterials.iteritems():
|
|
makeMeshMaterialCopy(matName, faces)
|
|
|
|
if len(materialFaces)!=len(myContextMesh_facels):
|
|
# Invert material faces.
|
|
makeMeshMaterialCopy(None, set(range(len( myContextMesh_facels ))) - materialFaces)
|
|
#raise 'Some UnMaterialed faces', len(contextMesh.faces)
|
|
|
|
#a spare chunk
|
|
new_chunk= chunk()
|
|
temp_chunk= chunk()
|
|
|
|
CreateBlenderObject = False
|
|
|
|
#loop through all the data for this chunk (previous chunk) and see what it is
|
|
while (previous_chunk.bytes_read<previous_chunk.length):
|
|
#print '\t', previous_chunk.bytes_read, 'keep going'
|
|
#read the next chunk
|
|
#print 'reading a chunk'
|
|
read_chunk(file, new_chunk)
|
|
|
|
#is it a Version chunk?
|
|
if (new_chunk.ID==VERSION):
|
|
#print 'if (new_chunk.ID==VERSION):'
|
|
#print 'found a VERSION chunk'
|
|
#read in the version of the file
|
|
#it's an unsigned short (H)
|
|
temp_data= file.read(calcsize('I'))
|
|
version = unpack('<I', temp_data)[0]
|
|
new_chunk.bytes_read+= 4 #read the 4 bytes for the version number
|
|
#this loader works with version 3 and below, but may not with 4 and above
|
|
if (version>3):
|
|
print '\tNon-Fatal Error: Version greater than 3, may not load correctly: ', version
|
|
|
|
#is it an object info chunk?
|
|
elif (new_chunk.ID==OBJECTINFO):
|
|
#print 'elif (new_chunk.ID==OBJECTINFO):'
|
|
# print 'found an OBJECTINFO chunk'
|
|
process_next_chunk(file, new_chunk, importedObjects, IMAGE_SEARCH)
|
|
|
|
#keep track of how much we read in the main chunk
|
|
new_chunk.bytes_read+=temp_chunk.bytes_read
|
|
|
|
#is it an object chunk?
|
|
elif (new_chunk.ID==OBJECT):
|
|
|
|
if CreateBlenderObject:
|
|
putContextMesh(contextMesh_vertls, contextMesh_facels, contextMeshMaterials)
|
|
contextMesh_vertls= []; contextMesh_facels= []
|
|
|
|
## preparando para receber o proximo objeto
|
|
contextMeshMaterials= {} # matname:[face_idxs]
|
|
contextMeshUV= None
|
|
#contextMesh.vertexUV= 1 # Make sticky coords.
|
|
# Reset matrix
|
|
contextMatrix_rot= None
|
|
#contextMatrix_tx= None
|
|
|
|
CreateBlenderObject= True
|
|
tempName= read_string(file)
|
|
contextObName= tempName
|
|
new_chunk.bytes_read += len(tempName)+1
|
|
|
|
#is it a material chunk?
|
|
elif (new_chunk.ID==MATERIAL):
|
|
#print 'elif (new_chunk.ID==MATERIAL):'
|
|
contextMaterial= bpy.data.materials.new('Material')
|
|
|
|
elif (new_chunk.ID==MAT_NAME):
|
|
#print 'elif (new_chunk.ID==MAT_NAME):'
|
|
material_name= read_string(file)
|
|
|
|
#plus one for the null character that ended the string
|
|
new_chunk.bytes_read+= len(material_name)+1
|
|
|
|
contextMaterial.name= material_name.rstrip() # remove trailing whitespace
|
|
MATDICT[material_name]= (contextMaterial.name, contextMaterial)
|
|
|
|
elif (new_chunk.ID==MAT_AMBIENT):
|
|
#print 'elif (new_chunk.ID==MAT_AMBIENT):'
|
|
read_chunk(file, temp_chunk)
|
|
if (temp_chunk.ID==MAT_FLOAT_COLOR):
|
|
temp_data=file.read(calcsize('3f'))
|
|
temp_chunk.bytes_read+=12
|
|
contextMaterial.mirCol=[float(col) for col in unpack('<3f', temp_data)]
|
|
elif (temp_chunk.ID==MAT_24BIT_COLOR):
|
|
temp_data=file.read(calcsize('3B'))
|
|
temp_chunk.bytes_read+= 3
|
|
contextMaterial.mirCol= [float(col)/255 for col in unpack('<3B', temp_data)] # data [0,1,2] == rgb
|
|
else:
|
|
skip_to_end(file, temp_chunk)
|
|
new_chunk.bytes_read+= temp_chunk.bytes_read
|
|
|
|
elif (new_chunk.ID==MAT_DIFFUSE):
|
|
#print 'elif (new_chunk.ID==MAT_DIFFUSE):'
|
|
read_chunk(file, temp_chunk)
|
|
if (temp_chunk.ID==MAT_FLOAT_COLOR):
|
|
temp_data=file.read(calcsize('3f'))
|
|
temp_chunk.bytes_read+=12
|
|
contextMaterial.rgbCol=[float(col) for col in unpack('<3f', temp_data)]
|
|
elif (temp_chunk.ID==MAT_24BIT_COLOR):
|
|
temp_data=file.read(calcsize('3B'))
|
|
temp_chunk.bytes_read+= 3
|
|
contextMaterial.rgbCol= [float(col)/255 for col in unpack('<3B', temp_data)] # data [0,1,2] == rgb
|
|
else:
|
|
skip_to_end(file, temp_chunk)
|
|
new_chunk.bytes_read+= temp_chunk.bytes_read
|
|
|
|
elif (new_chunk.ID==MAT_SPECULAR):
|
|
#print 'elif (new_chunk.ID==MAT_SPECULAR):'
|
|
read_chunk(file, temp_chunk)
|
|
if (temp_chunk.ID==MAT_FLOAT_COLOR):
|
|
temp_data=file.read(calcsize('3f'))
|
|
temp_chunk.bytes_read+=12
|
|
contextMaterial.mirCol=[float(col) for col in unpack('<3f', temp_data)]
|
|
elif (temp_chunk.ID==MAT_24BIT_COLOR):
|
|
temp_data=file.read(calcsize('3B'))
|
|
temp_chunk.bytes_read+= 3
|
|
contextMaterial.mirCol= [float(col)/255 for col in unpack('<3B', temp_data)] # data [0,1,2] == rgb
|
|
else:
|
|
skip_to_end(file, temp_chunk)
|
|
new_chunk.bytes_read+= temp_chunk.bytes_read
|
|
|
|
elif (new_chunk.ID==MAT_TEXTURE_MAP):
|
|
#print 'elif (new_chunk.ID==MAT_TEXTURE_MAP):'
|
|
new_texture= bpy.data.textures.new('Diffuse')
|
|
new_texture.setType('Image')
|
|
img = None
|
|
while (new_chunk.bytes_read<new_chunk.length):
|
|
#print 'MAT_TEXTURE_MAP..while', new_chunk.bytes_read, new_chunk.length
|
|
read_chunk(file, temp_chunk)
|
|
|
|
if (temp_chunk.ID==MAT_MAP_FILENAME):
|
|
texture_name=read_string(file)
|
|
#img= TEXTURE_DICT[contextMaterial.name]= BPyImage.comprehensiveImageLoad(texture_name, FILENAME)
|
|
img= TEXTURE_DICT[contextMaterial.name]= BPyImage.comprehensiveImageLoad(texture_name, FILENAME, PLACE_HOLDER= False, RECURSIVE= IMAGE_SEARCH)
|
|
new_chunk.bytes_read += (len(texture_name)+1) #plus one for the null character that gets removed
|
|
|
|
else:
|
|
skip_to_end(file, temp_chunk)
|
|
|
|
new_chunk.bytes_read+= temp_chunk.bytes_read
|
|
|
|
#add the map to the material in the right channel
|
|
if img:
|
|
add_texture_to_material(img, new_texture, contextMaterial, 'DIFFUSE')
|
|
|
|
elif (new_chunk.ID==MAT_SPECULAR_MAP):
|
|
#print 'elif (new_chunk.ID==MAT_SPECULAR_MAP):'
|
|
new_texture= bpy.data.textures.new('Specular')
|
|
new_texture.setType('Image')
|
|
img = None
|
|
while (new_chunk.bytes_read<new_chunk.length):
|
|
read_chunk(file, temp_chunk)
|
|
|
|
if (temp_chunk.ID==MAT_MAP_FILENAME):
|
|
texture_name= read_string(file)
|
|
#img= BPyImage.comprehensiveImageLoad(texture_name, FILENAME)
|
|
img= BPyImage.comprehensiveImageLoad(texture_name, FILENAME, PLACE_HOLDER= False, RECURSIVE= IMAGE_SEARCH)
|
|
new_chunk.bytes_read+= (len(texture_name)+1) #plus one for the null character that gets removed
|
|
else:
|
|
skip_to_end(file, temp_chunk)
|
|
|
|
new_chunk.bytes_read+= temp_chunk.bytes_read
|
|
|
|
#add the map to the material in the right channel
|
|
if img:
|
|
add_texture_to_material(img, new_texture, contextMaterial, 'SPECULAR')
|
|
|
|
elif (new_chunk.ID==MAT_OPACITY_MAP):
|
|
#print 'new_texture=Blender.Texture.New('Opacity')'
|
|
new_texture= bpy.data.textures.new('Opacity')
|
|
new_texture.setType('Image')
|
|
img = None
|
|
while (new_chunk.bytes_read<new_chunk.length):
|
|
read_chunk(file, temp_chunk)
|
|
|
|
if (temp_chunk.ID==MAT_MAP_FILENAME):
|
|
texture_name= read_string(file)
|
|
#img= BPyImage.comprehensiveImageLoad(texture_name, FILENAME)
|
|
img= BPyImage.comprehensiveImageLoad(texture_name, FILENAME, PLACE_HOLDER= False, RECURSIVE= IMAGE_SEARCH)
|
|
new_chunk.bytes_read += (len(texture_name)+1) #plus one for the null character that gets removed
|
|
else:
|
|
skip_to_end(file, temp_chunk)
|
|
|
|
new_chunk.bytes_read+= temp_chunk.bytes_read
|
|
#add the map to the material in the right channel
|
|
if img:
|
|
add_texture_to_material(img, new_texture, contextMaterial, 'OPACITY')
|
|
|
|
elif (new_chunk.ID==MAT_BUMP_MAP):
|
|
#print 'elif (new_chunk.ID==MAT_BUMP_MAP):'
|
|
new_texture= bpy.data.textures.new('Bump')
|
|
new_texture.setType('Image')
|
|
img = None
|
|
while (new_chunk.bytes_read<new_chunk.length):
|
|
read_chunk(file, temp_chunk)
|
|
|
|
if (temp_chunk.ID==MAT_MAP_FILENAME):
|
|
texture_name= read_string(file)
|
|
#img= BPyImage.comprehensiveImageLoad(texture_name, FILENAME)
|
|
img= BPyImage.comprehensiveImageLoad(texture_name, FILENAME, PLACE_HOLDER= False, RECURSIVE= IMAGE_SEARCH)
|
|
new_chunk.bytes_read += (len(texture_name)+1) #plus one for the null character that gets removed
|
|
else:
|
|
skip_to_end(file, temp_chunk)
|
|
|
|
new_chunk.bytes_read+=temp_chunk.bytes_read
|
|
|
|
#add the map to the material in the right channel
|
|
if img:
|
|
add_texture_to_material(img, new_texture, contextMaterial, 'BUMP')
|
|
|
|
elif (new_chunk.ID==MAT_TRANSPARENCY):
|
|
#print 'elif (new_chunk.ID==MAT_TRANSPARENCY):'
|
|
read_chunk(file, temp_chunk)
|
|
temp_data=file.read(STRUCT_SIZE_UNSIGNED_SHORT)
|
|
|
|
temp_chunk.bytes_read+=2
|
|
contextMaterial.alpha= 1-(float(unpack('<H', temp_data)[0])/100)
|
|
new_chunk.bytes_read+=temp_chunk.bytes_read
|
|
|
|
|
|
elif (new_chunk.ID==OBJECT_LAMP): # Basic lamp support.
|
|
|
|
temp_data=file.read(STRUCT_SIZE_3FLOAT)
|
|
|
|
x,y,z=unpack('<3f', temp_data)
|
|
new_chunk.bytes_read+=STRUCT_SIZE_3FLOAT
|
|
|
|
contextLamp[1]= bpy.data.lamps.new()
|
|
contextLamp[0]= SCN_OBJECTS.new(contextLamp[1])
|
|
importedObjects.append(contextLamp[0])
|
|
|
|
#print 'number of faces: ', num_faces
|
|
#print x,y,z
|
|
contextLamp[0].setLocation(x,y,z)
|
|
|
|
# Reset matrix
|
|
contextMatrix_rot= None
|
|
#contextMatrix_tx= None
|
|
#print contextLamp.name,
|
|
|
|
elif (new_chunk.ID==OBJECT_MESH):
|
|
# print 'Found an OBJECT_MESH chunk'
|
|
pass
|
|
elif (new_chunk.ID==OBJECT_VERTICES):
|
|
'''
|
|
Worldspace vertex locations
|
|
'''
|
|
# print 'elif (new_chunk.ID==OBJECT_VERTICES):'
|
|
temp_data=file.read(STRUCT_SIZE_UNSIGNED_SHORT)
|
|
num_verts=unpack('<H', temp_data)[0]
|
|
new_chunk.bytes_read+=2
|
|
|
|
# print 'number of verts: ', num_verts
|
|
def getvert():
|
|
temp_data= unpack('<3f', file.read(STRUCT_SIZE_3FLOAT))
|
|
new_chunk.bytes_read += STRUCT_SIZE_3FLOAT #12: 3 floats x 4 bytes each
|
|
return temp_data
|
|
|
|
#contextMesh.verts.extend( [Vector(),] ) # DUMMYVERT! - remove when blenders internals are fixed.
|
|
contextMesh_vertls= [getvert() for i in xrange(num_verts)]
|
|
|
|
#print 'object verts: bytes read: ', new_chunk.bytes_read
|
|
|
|
elif (new_chunk.ID==OBJECT_FACES):
|
|
# print 'elif (new_chunk.ID==OBJECT_FACES):'
|
|
temp_data= file.read(STRUCT_SIZE_UNSIGNED_SHORT)
|
|
num_faces= unpack('<H', temp_data)[0]
|
|
new_chunk.bytes_read+= 2
|
|
#print 'number of faces: ', num_faces
|
|
|
|
def getface():
|
|
# print '\ngetting a face'
|
|
temp_data= file.read(STRUCT_SIZE_4UNSIGNED_SHORT)
|
|
new_chunk.bytes_read+= STRUCT_SIZE_4UNSIGNED_SHORT #4 short ints x 2 bytes each
|
|
v1,v2,v3,dummy= unpack('<4H', temp_data)
|
|
return v1, v2, v3
|
|
|
|
contextMesh_facels= [ getface() for i in xrange(num_faces) ]
|
|
|
|
|
|
elif (new_chunk.ID==OBJECT_MATERIAL):
|
|
# print 'elif (new_chunk.ID==OBJECT_MATERIAL):'
|
|
material_name= read_string(file)
|
|
new_chunk.bytes_read += len(material_name)+1 # remove 1 null character.
|
|
|
|
temp_data=file.read(STRUCT_SIZE_UNSIGNED_SHORT)
|
|
num_faces_using_mat = unpack('<H', temp_data)[0]
|
|
new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT
|
|
|
|
def getmat():
|
|
temp_data= file.read(STRUCT_SIZE_UNSIGNED_SHORT)
|
|
new_chunk.bytes_read+= STRUCT_SIZE_UNSIGNED_SHORT
|
|
return unpack('<H', temp_data)[0]
|
|
|
|
contextMeshMaterials[material_name]= [ getmat() for i in xrange(num_faces_using_mat) ]
|
|
|
|
#look up the material in all the materials
|
|
|
|
elif (new_chunk.ID==OBJECT_UV):
|
|
temp_data=file.read(STRUCT_SIZE_UNSIGNED_SHORT)
|
|
num_uv=unpack('<H', temp_data)[0]
|
|
new_chunk.bytes_read+= 2
|
|
|
|
def getuv():
|
|
temp_data=file.read(STRUCT_SIZE_2FLOAT)
|
|
new_chunk.bytes_read += STRUCT_SIZE_2FLOAT #2 float x 4 bytes each
|
|
return Vector( unpack('<2f', temp_data) )
|
|
|
|
contextMeshUV= [ getuv() for i in xrange(num_uv) ]
|
|
|
|
elif (new_chunk.ID== OBJECT_TRANS_MATRIX):
|
|
# How do we know the matrix size? 54 == 4x4 48 == 4x3
|
|
temp_data=file.read(STRUCT_SIZE_4x3MAT)
|
|
data= list( unpack('<ffffffffffff', temp_data) )
|
|
new_chunk.bytes_read += STRUCT_SIZE_4x3MAT
|
|
|
|
contextMatrix_rot= Blender.Mathutils.Matrix(\
|
|
data[:3] + [0],\
|
|
data[3:6] + [0],\
|
|
data[6:9] + [0],\
|
|
data[9:] + [1])
|
|
|
|
|
|
'''
|
|
contextMatrix_rot= Blender.Mathutils.Matrix(\
|
|
data[:3] + [0],\
|
|
data[3:6] + [0],\
|
|
data[6:9] + [0],\
|
|
[0,0,0,1])
|
|
'''
|
|
|
|
'''
|
|
contextMatrix_rot= Blender.Mathutils.Matrix(\
|
|
data[:3] ,\
|
|
data[3:6],\
|
|
data[6:9])
|
|
'''
|
|
|
|
'''
|
|
contextMatrix_rot = Blender.Mathutils.Matrix()
|
|
m = 0
|
|
for j in xrange(4):
|
|
for i in xrange(3):
|
|
contextMatrix_rot[j][i] = data[m]
|
|
m+=1
|
|
|
|
contextMatrix_rot[0][3]=0;
|
|
contextMatrix_rot[1][3]=0;
|
|
contextMatrix_rot[2][3]=0;
|
|
contextMatrix_rot[3][3]=1;
|
|
'''
|
|
|
|
#contextMatrix_rot.resize4x4()
|
|
#print "MTX"
|
|
#print contextMatrix_rot
|
|
contextMatrix_rot.invert()
|
|
#print contextMatrix_rot
|
|
#contextMatrix_tx = Blender.Mathutils.TranslationMatrix(0.5 * Blender.Mathutils.Vector(data[9:]))
|
|
#contextMatrix_tx.invert()
|
|
|
|
#tx.invert()
|
|
|
|
#contextMatrix = contextMatrix * tx
|
|
#contextMatrix = contextMatrix *tx
|
|
|
|
elif (new_chunk.ID==MAT_MAP_FILENAME):
|
|
texture_name=read_string(file)
|
|
try:
|
|
TEXTURE_DICT[contextMaterial.name]
|
|
except:
|
|
#img= TEXTURE_DICT[contextMaterial.name]= BPyImage.comprehensiveImageLoad(texture_name, FILENAME)
|
|
img= TEXTURE_DICT[contextMaterial.name]= BPyImage.comprehensiveImageLoad(texture_name, FILENAME, PLACE_HOLDER= False, RECURSIVE= IMAGE_SEARCH)
|
|
|
|
new_chunk.bytes_read+= len(texture_name)+1 #plus one for the null character that gets removed
|
|
|
|
else: #(new_chunk.ID!=VERSION or new_chunk.ID!=OBJECTINFO or new_chunk.ID!=OBJECT or new_chunk.ID!=MATERIAL):
|
|
# print 'skipping to end of this chunk'
|
|
buffer_size=new_chunk.length-new_chunk.bytes_read
|
|
binary_format='%ic' % buffer_size
|
|
temp_data=file.read(calcsize(binary_format))
|
|
new_chunk.bytes_read+=buffer_size
|
|
|
|
|
|
#update the previous chunk bytes read
|
|
# print 'previous_chunk.bytes_read += new_chunk.bytes_read'
|
|
# print previous_chunk.bytes_read, new_chunk.bytes_read
|
|
previous_chunk.bytes_read += new_chunk.bytes_read
|
|
## print 'Bytes left in this chunk: ', previous_chunk.length-previous_chunk.bytes_read
|
|
|
|
# FINISHED LOOP
|
|
# There will be a number of objects still not added
|
|
if contextMesh_facels != None:
|
|
putContextMesh(contextMesh_vertls, contextMesh_facels, contextMeshMaterials)
|
|
|
|
def load_3ds(filename, PREF_UI= True):
|
|
global FILENAME, SCN_OBJECTS
|
|
|
|
if BPyMessages.Error_NoFile(filename):
|
|
return
|
|
|
|
print '\n\nImporting 3DS: "%s"' % (Blender.sys.expandpath(filename))
|
|
|
|
time1= Blender.sys.time()
|
|
|
|
FILENAME=filename
|
|
current_chunk=chunk()
|
|
|
|
file=open(filename,'rb')
|
|
|
|
#here we go!
|
|
# print 'reading the first chunk'
|
|
read_chunk(file, current_chunk)
|
|
if (current_chunk.ID!=PRIMARY):
|
|
print '\tFatal Error: Not a valid 3ds file: ', filename
|
|
file.close()
|
|
return
|
|
|
|
|
|
# IMPORT_AS_INSTANCE= Blender.Draw.Create(0)
|
|
IMPORT_CONSTRAIN_BOUNDS= Blender.Draw.Create(10.0)
|
|
IMAGE_SEARCH= Blender.Draw.Create(1)
|
|
APPLY_MATRIX= Blender.Draw.Create(0)
|
|
|
|
# Get USER Options
|
|
pup_block= [\
|
|
('Size Constraint:', IMPORT_CONSTRAIN_BOUNDS, 0.0, 1000.0, 'Scale the model by 10 until it reacehs the size constraint. Zero Disables.'),\
|
|
('Image Search', IMAGE_SEARCH, 'Search subdirs for any assosiated images (Warning, may be slow)'),\
|
|
('Transform Fix', APPLY_MATRIX, 'Workaround for object transformations importing incorrectly'),\
|
|
#('Group Instance', IMPORT_AS_INSTANCE, 'Import objects into a new scene and group, creating an instance in the current scene.'),\
|
|
]
|
|
|
|
if PREF_UI:
|
|
if not Blender.Draw.PupBlock('Import 3DS...', pup_block):
|
|
return
|
|
|
|
Blender.Window.WaitCursor(1)
|
|
|
|
IMPORT_CONSTRAIN_BOUNDS= IMPORT_CONSTRAIN_BOUNDS.val
|
|
# IMPORT_AS_INSTANCE= IMPORT_AS_INSTANCE.val
|
|
IMAGE_SEARCH = IMAGE_SEARCH.val
|
|
APPLY_MATRIX = APPLY_MATRIX.val
|
|
|
|
if IMPORT_CONSTRAIN_BOUNDS:
|
|
BOUNDS_3DS[:]= [1<<30, 1<<30, 1<<30, -1<<30, -1<<30, -1<<30]
|
|
else:
|
|
BOUNDS_3DS[:]= []
|
|
|
|
##IMAGE_SEARCH
|
|
|
|
scn= bpy.data.scenes.active
|
|
SCN_OBJECTS = scn.objects
|
|
SCN_OBJECTS.selected = [] # de select all
|
|
|
|
importedObjects= [] # Fill this list with objects
|
|
process_next_chunk(file, current_chunk, importedObjects, IMAGE_SEARCH)
|
|
|
|
|
|
# Link the objects into this scene.
|
|
# Layers= scn.Layers
|
|
|
|
# REMOVE DUMMYVERT, - remove this in the next release when blenders internal are fixed.
|
|
|
|
|
|
for ob in importedObjects:
|
|
if ob.type=='Mesh':
|
|
me= ob.getData(mesh=1)
|
|
me.verts.delete([me.verts[0],])
|
|
if not APPLY_MATRIX:
|
|
me.transform(ob.matrixWorld.copy().invert())
|
|
|
|
# Done DUMMYVERT
|
|
"""
|
|
if IMPORT_AS_INSTANCE:
|
|
name= filename.split('\\')[-1].split('/')[-1]
|
|
# Create a group for this import.
|
|
group_scn= Scene.New(name)
|
|
for ob in importedObjects:
|
|
group_scn.link(ob) # dont worry about the layers
|
|
|
|
grp= Blender.Group.New(name)
|
|
grp.objects= importedObjects
|
|
|
|
grp_ob= Object.New('Empty', name)
|
|
grp_ob.enableDupGroup= True
|
|
grp_ob.DupGroup= grp
|
|
scn.link(grp_ob)
|
|
grp_ob.Layers= Layers
|
|
grp_ob.sel= 1
|
|
else:
|
|
# Select all imported objects.
|
|
for ob in importedObjects:
|
|
scn.link(ob)
|
|
ob.Layers= Layers
|
|
ob.sel= 1
|
|
"""
|
|
|
|
if IMPORT_CONSTRAIN_BOUNDS!=0.0:
|
|
# Set bounds from objecyt bounding box
|
|
for ob in importedObjects:
|
|
if ob.type=='Mesh':
|
|
ob.makeDisplayList() # Why dosnt this update the bounds?
|
|
for v in ob.getBoundBox():
|
|
for i in (0,1,2):
|
|
if v[i] < BOUNDS_3DS[i]:
|
|
BOUNDS_3DS[i]= v[i] # min
|
|
|
|
if v[i] > BOUNDS_3DS[i+3]:
|
|
BOUNDS_3DS[i+3]= v[i] # min
|
|
|
|
# Get the max axis x/y/z
|
|
max_axis= max(BOUNDS_3DS[3]-BOUNDS_3DS[0], BOUNDS_3DS[4]-BOUNDS_3DS[1], BOUNDS_3DS[5]-BOUNDS_3DS[2])
|
|
# print max_axis
|
|
if max_axis < 1<<30: # Should never be false but just make sure.
|
|
|
|
# Get a new scale factor if set as an option
|
|
SCALE=1.0
|
|
while (max_axis*SCALE) > IMPORT_CONSTRAIN_BOUNDS:
|
|
SCALE/=10
|
|
|
|
# SCALE Matrix
|
|
SCALE_MAT= Blender.Mathutils.Matrix([SCALE,0,0,0],[0,SCALE,0,0],[0,0,SCALE,0],[0,0,0,1])
|
|
|
|
for ob in importedObjects:
|
|
ob.setMatrix(ob.matrixWorld*SCALE_MAT)
|
|
|
|
# Done constraining to bounds.
|
|
|
|
# Select all new objects.
|
|
print 'finished importing: "%s" in %.4f sec.' % (filename, (Blender.sys.time()-time1))
|
|
file.close()
|
|
Blender.Window.WaitCursor(0)
|
|
|
|
|
|
DEBUG= False
|
|
if __name__=='__main__' and not DEBUG:
|
|
Blender.Window.FileSelector(load_3ds, 'Import 3DS', '*.3ds')
|
|
|
|
# For testing compatibility
|
|
#load_3ds('/metavr/convert/vehicle/truck_002/TruckTanker1.3DS', False)
|
|
#load_3ds('/metavr/archive/convert/old/arranged_3ds_to_hpx-2/only-need-engine-trains/Engine2.3DS', False)
|
|
'''
|
|
|
|
else:
|
|
# DEBUG ONLY
|
|
TIME= Blender.sys.time()
|
|
import os
|
|
print 'Searching for files'
|
|
os.system('find /metavr/ -iname "*.3ds" > /tmp/temp3ds_list')
|
|
# os.system('find /storage/ -iname "*.3ds" > /tmp/temp3ds_list')
|
|
print '...Done'
|
|
file= open('/tmp/temp3ds_list', 'r')
|
|
lines= file.readlines()
|
|
file.close()
|
|
# sort by filesize for faster testing
|
|
lines_size = [(os.path.getsize(f[:-1]), f[:-1]) for f in lines]
|
|
lines_size.sort()
|
|
lines = [f[1] for f in lines_size]
|
|
|
|
|
|
def between(v,a,b):
|
|
if v <= max(a,b) and v >= min(a,b):
|
|
return True
|
|
return False
|
|
|
|
for i, _3ds in enumerate(lines):
|
|
if between(i, 650,800):
|
|
#_3ds= _3ds[:-1]
|
|
print 'Importing', _3ds, '\nNUMBER', i, 'of', len(lines)
|
|
_3ds_file= _3ds.split('/')[-1].split('\\')[-1]
|
|
newScn= Blender.Scene.New(_3ds_file)
|
|
newScn.makeCurrent()
|
|
load_3ds(_3ds, False)
|
|
|
|
print 'TOTAL TIME: %.6f' % (Blender.sys.time() - TIME)
|
|
|
|
'''
|