pviktori / rpms / blender

Forked from rpms/blender 5 years ago
Clone
Blob Blame History Raw
#!BPY

"""
Name: '3D Studio'
Blender: 233
Group: 'Export'
Tip: 'Export to 3DS file format. (.3ds)'
"""



######################################################
# 3ds Importer
# By:  Bob Holcomb and Richard Lärkäng
# Date: 22 APR 04
# Ver: 0.7
######################################################
# This script imports a 3ds file and the materials
#  into blender for editing.  Hopefully
# this will make it into a future version as an import
# feature of the menu.  Loader is based on 3ds loader
# from www.gametutorials.com(Thanks DigiBen).
######################################################

######################################################
# Importing modules
######################################################

import Blender
from Blender import NMesh, Scene, Object, Material
from Blender.BGL import *
from Blender.Draw import *
from Blender.Window import *
from Blender.Image import *
from Blender.Material import *

import sys, struct, string, types, math
from types import *

import os
from os import path


######################################################
# Data Structures
######################################################

#Some of the chunks that we will see
#----- Primary Chunk, at the beginning of each file
PRIMARY=19789 				#0x4D4D

#------ Main Chunks
OBJECTINFO=15677				#0x3D3D			// This gives the version of the mesh and is found right before the material and object information
VERSION=2					#0x0002			// This gives the version of the .3ds file
EDITKEYFRAME=45056			#0xB000			// 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
MATNAME=40960				#0xA000			// This holds the material name
MATAMBIENT=40976			#0xA010
MATDIFFUSE=40992				#0xA020			// This holds the color of the object/material
MATSPECULAR=41008			#0xA030
MATMAP=41472				#0xA200			// This is a header for a new material
MATMAPFILE=41728				#0xA300			// This holds the file name of the texture

RGB1=17					#0x0011
RGB2=18					#0x0012

OBJECT_MESH=16640			#0x4100			// This lets us know that we are reading a new object

#------ sub defines of OBJECT_MESH
OBJECT_VERTICES=16656			#0x4110			// The objects vertices
OBJECT_FACES=16672			#0x4120			// The objects faces
OBJECT_MATERIAL=16688		#0x4130			// This is found if the object has a material, either texture map or color
OBJECT_UV=16704				#0x4140			// The UV texture coordinates
OBJECT_TRANS_MATRIX=16736	#0x4160			// The translation matrix of the object 54 bytes

def point_by_matrix(p, m):
  return [p[0] * m[0][0] + p[1] * m[1][0] + p[2] * m[2][0] + m[3][0],
          p[0] * m[0][1] + p[1] * m[1][1] + p[2] * m[2][1] + m[3][1],
          p[0] * m[0][2] + p[1] * m[1][2] + p[2] * m[2][2] + m[3][2]]

#the chunk class
class chunk:
	ID=0
	size=0

	def __init__(self):
		self.ID=0
		self.size=0

	def get_size(self):
		self.size=6

	def write(self, file):
		binary_format="<HI"  #all header info looks like this
		#calculate size
		temp_data=[0]*2
		temp_data[0]=self.ID
		temp_data[1]=self.size
		#write header
		data=struct.pack(binary_format,temp_data[0],temp_data[1])
		file.write(data)

	def dump(self):
		print "ID: ", self.ID
		print "ID in hex: ", hex(self.ID)
		print "size: ", self.size



#may want to add light, camera, keyframe chunks.
class vert_chunk(chunk):
	verts=[]

	def __init__(self):
		self.verts=[]
		self.ID=OBJECT_VERTICES

	def get_size(self):
		chunk.get_size(self)
		temp_size=2 #for the number of verts short
		for vert in self.verts:
			temp_size+=12  #3 floats x 4 bytes each
		self.size+=temp_size
		print "vert_chunk size: ", self.size
		return self.size

	def write(self, file):
		chunk.write(self, file)
		binary_format="<H"
		temp_data=[0]
		temp_data[0]=len(self.verts)
		#write header
		data=struct.pack(binary_format,temp_data[0])
		file.write(data)
		#write verts
		for vert in self.verts:
			binary_format="<3f"
			temp_data=[0.0]*3
			temp_data[0]=vert[0]
			temp_data[1]=vert[1]
			temp_data[2]=vert[2]
			data=struct.pack(binary_format,temp_data[0],temp_data[1], temp_data[2])
			file.write(data)

class obj_material_chunk(chunk):
	name=""
	faces=[]

	def __init__(self):
		self.name=""
		self.faces=[]
		self.ID=OBJECT_MATERIAL

	def get_size(self):
		chunk.get_size(self)
		temp_size=(len(self.name)+1)
		temp_size+=2
		for face in self.faces:
			temp_size+=2
		self.size+=temp_size
		print "obj material chunk size: ", self.size
		return self.size

	def write(self, file):
		chunk.write(self, file)
		#write name
		name_length=len(self.name)+1
		binary_format="<"+str(name_length)+"s"
		data=struct.pack(binary_format, self.name)
		file.write(data)
		binary_format="<H"
		print "Nr of faces: ", len(self.faces)
		data=struct.pack(binary_format, len(self.faces))
		file.write(data)
		for face in self.faces:
			data=struct.pack(binary_format, face)
			file.write(data)

class face_chunk(chunk):
	faces=[]
	num_faces=0
	m_chunks=[]

	def __init__(self):
		self.faces=[]
		self.ID=OBJECT_FACES
		self.num_faces=0
		self.m_chunks=[]

	def get_size(self):
		chunk.get_size(self)
		temp_size=2 #num faces info
		for face in self.faces:
			temp_size+=8  #4 short ints x 2 bytes each
		for m in self.m_chunks:
			temp_size+=m.get_size()
		self.size+=temp_size
		print "face_chunk size: ", self.size
		return self.size

	def write(self, file):
		chunk.write(self, file)
		binary_format="<H"
		temp_data=[0]
		temp_data[0]=len(self.faces)
		data=struct.pack(binary_format,temp_data[0])
		file.write(data)
		#write faces
		for face in self.faces:
			binary_format="<4H"
			temp_data=[0]*4
			temp_data[0]=face[0]
			temp_data[1]=face[1]
			temp_data[2]=face[2]
			temp_data[3]=0 #only used by 3d studio
			data=struct.pack(binary_format,temp_data[0],temp_data[1], temp_data[2], temp_data[3])
			file.write(data)
		#write materials
		for m in self.m_chunks:
			m.write(file)

class uv_chunk(chunk):
	uv=[]
	num_uv=0

	def __init__(self):
		self.uv=[]
		self.ID=OBJECT_UV
		self.num_uv=0

	def get_size(self):
		chunk.get_size(self)
		temp_size=2 #for num UV
		for this_uv in self.uv:
			temp_size+=8  #2 floats at 4 bytes each
		self.size+=temp_size
		print "uv chunk size: ", self.size
		return self.size

	def write(self, file):
		chunk.write(self, file)
		binary_format="<H"
		temp_data=[0]
		temp_data[0]=len(self.uv)
		#write header
		data=struct.pack(binary_format,temp_data[0])
		file.write(data)
		#write verts
		for this_uv in self.uv:
			binary_format="<2f"
			temp_data=[0.0]*2
			temp_data[0]=this_uv[0]
			temp_data[1]=this_uv[1]
			data=struct.pack(binary_format,temp_data[0],temp_data[1])
			file.write(data)

class mesh_chunk(chunk):
	v_chunk=vert_chunk()
	f_chunk=face_chunk()
	uv_chunk=uv_chunk()

	def __init__(self):
		self.v_chunk=vert_chunk()
		self.f_chunk=face_chunk()
		self.uv_chunk=uv_chunk()
		self.ID=OBJECT_MESH

	def get_size(self):
		chunk.get_size(self)
		temp_size=self.v_chunk.get_size()
		temp_size+=self.f_chunk.get_size()
		temp_size+=self.uv_chunk.get_size()
		self.size+=temp_size
		print "object mesh chunk size: ", self.size
		return self.size

	def write(self, file):
		chunk.write(self, file)
		#write stuff
		self.v_chunk.write(file)
		self.f_chunk.write(file)
		self.uv_chunk.write(file)

class object_chunk(chunk):
	name=""
	mesh_chunks=[]

	def __init__(self):
		self.name=""
		self.mesh_chunks=[]
		self.ID=OBJECT

	def get_size(self):
		chunk.get_size(self)
		temp_size=len(self.name)+1 #+1 for null character
		for mesh in self.mesh_chunks:
			temp_size+=mesh.get_size()
		self.size+=temp_size
		print "object chunk size: ", self.size
		return self.size

	def write(self, file):
		chunk.write(self, file)
		#write name
		name_length=len(self.name)+1
		binary_format="<"+str(name_length)+"s"
		data=struct.pack(binary_format, self.name)
		file.write(data)
		#write stuff
		for mesh in self.mesh_chunks:
			mesh.write(file)

class object_info_chunk(chunk):
	obj_chunks=[]
	mat_chunks=[]

	def __init__(self):
		self.obj_chunks=[]
		self.mat_chunks=[]
		self.ID=OBJECTINFO

	def get_size(self):
		chunk.get_size(self)
		temp_size=0
		for mat in self.mat_chunks:
			temp_size+=mat.get_size()
		for obj in self.obj_chunks:
			temp_size+=obj.get_size()
		self.size+=temp_size
		print "object info size: ", self.size
		return self.size

	def write(self, file):
		chunk.write(self, file)
		#write all the materials
		for mat in self.mat_chunks:
			mat.write(file)
		#write all the objects
		for obj in self.obj_chunks:
			obj.write(file)



class version_chunk(chunk):
	version=3

	def __init__(self):
		self.ID=VERSION
		self.version=3 #that the document that I'm using

	def get_size(self):
		chunk.get_size(self)
		temp_size=4 #bytes for the version info
		self.size+=temp_size
		print "version chunk size: ", self.size
		return self.size

	def write(self, file):
		chunk.write(self, file)
		binary_format="<I"
		temp_data=[0]
		temp_data[0]=self.version
		#write header and version
		data=struct.pack(binary_format,temp_data[0])
		file.write(data)

class rgb_chunk(chunk):
	col=[]

	def __init__(self):
		self.col=[]

	def get_size(self):
		chunk.get_size(self)
		temp_size=3  #color size
		self.size+=temp_size
		print "rgb chunk size: ", self.size
		return self.size

	def write(self, file):
		chunk.write(self, file)
		binary_format="<c"
		#write colors
		for i in [0, 1, 2]:
			data=struct.pack(binary_format,chr(255*self.col[i]))
			file.write(data)


class rgb1_chunk(rgb_chunk):

	def __init__(self):
		self.ID=RGB1

class rgb2_chunk(rgb_chunk):

	def __init__(self):
		self.ID=RGB2

class material_ambient_chunk(chunk):
	col1=None
	col2=None

	def __init__(self):
		self.ID=MATAMBIENT
		self.col1=rgb1_chunk()
		self.col2=rgb2_chunk()

	def get_size(self):
		chunk.get_size(self)
		temp_size=self.col1.get_size()
		temp_size+=self.col2.get_size()
		self.size+=temp_size
		print "material ambient size: ", self.size
		return self.size

	def write(self, file):
		chunk.write(self, file)
		#write colors
		self.col1.write(file)
		self.col2.write(file)

class material_diffuse_chunk(chunk):
	col1=None
	col2=None

	def __init__(self):
		self.ID=MATDIFFUSE
		self.col1=rgb1_chunk()
		self.col2=rgb2_chunk()

	def get_size(self):
		chunk.get_size(self)
		temp_size=self.col1.get_size()
		temp_size+=self.col2.get_size()
		self.size+=temp_size
		print "material diffuse size: ", self.size
		return self.size

	def write(self, file):
		chunk.write(self, file)
		#write colors
		self.col1.write(file)
		self.col2.write(file)

class material_specular_chunk(chunk):
	col1=None
	col2=None

	def __init__(self):
		self.ID=MATSPECULAR
		self.col1=rgb1_chunk()
		self.col2=rgb2_chunk()

	def get_size(self):
		chunk.get_size(self)
		temp_size=self.col1.get_size()
		temp_size+=self.col2.get_size()
		self.size+=temp_size
		print "material specular size: ", self.size
		return self.size

	def write(self, file):
		chunk.write(self, file)
		#write colors
		self.col1.write(file)
		self.col2.write(file)

class material_name_chunk(chunk):
	name=""

	def __init__(self):
		self.ID=MATNAME
		self.name=""

	def get_size(self):
		chunk.get_size(self)
		temp_size=(len(self.name)+1)
		self.size+=temp_size
		print "material name size: ", self.size
		return self.size

	def write(self, file):
		chunk.write(self, file)
		#write name
		name_length=len(self.name)+1
		binary_format="<"+str(name_length)+"s"
		data=struct.pack(binary_format, self.name)
		file.write(data)

class material_chunk(chunk):
	matname_chunk=None
	matambient_chunk=None
	matdiffuse_chunk=None
	matspecular_chunk=None

	def __init__(self):
		self.ID=MATERIAL
		self.matname_chunk=material_name_chunk()
		self.matambient_chunk=material_ambient_chunk()
		self.matdiffuse_chunk=material_diffuse_chunk()
		self.matspecular_chunk=material_specular_chunk()

	def get_size(self):
		chunk.get_size(self)
		temp_size=self.matname_chunk.get_size()
		temp_size+=self.matambient_chunk.get_size()
		temp_size+=self.matdiffuse_chunk.get_size()
		temp_size+=self.matspecular_chunk.get_size()
		self.size+=temp_size
		print "material chunk size: ", self.size
		return self.size

	def write(self, file):
		chunk.write(self, file)
		#write name chunk
		self.matname_chunk.write(file)
		#write material colors
		self.matambient_chunk.write(file)
		self.matdiffuse_chunk.write(file)
		self.matspecular_chunk.write(file)

class primary_chunk(chunk):
	version=None
	obj_info=None

	def __init__(self):
		self.version=version_chunk()
		self.obj_info=object_info_chunk()
		self.ID=PRIMARY

	def get_size(self):
		chunk.get_size(self)
		temp_size=self.version.get_size()
		temp_size+=self.obj_info.get_size()
		self.size+=temp_size
		print "primary chunk size: ", self.size
		return self.size

	def write(self, file):
		chunk.write(self, file)
		#write version chunk
		self.version.write(file)
		#write object_info chunk
		self.obj_info.write(file)

def read_chunk(file, chunk):
		temp_data=file.read(struct.calcsize(chunk.binary_format))
		data=struct.unpack(chunk.binary_format, temp_data)
		chunk.ID=data[0]
		chunk.size=data[1]

		#if debugging
		#chunk.dump()

def read_string(file):
	s=""
	index=0
	#print "reading a string"
	#read in the characters till we get a null character
	temp_data=file.read(struct.calcsize("c"))
	data=struct.unpack("c", temp_data)
	s=s+(data[0])
	#print "string: ",s
	while(ord(s[index])!=0):
		index+=1
		temp_data=file.read(struct.calcsize("c"))
		data=struct.unpack("c", temp_data)
		s=s+(data[0])
		#print "string: ",s
	return str(s)

######################################################
# EXPORT
######################################################
def save_3ds(filename):

	vert_list={}
	vert_count=0
	#open the current scene
	scene = Blender.Scene.GetCurrent()
	#if the user was lazy and didn't add a filename,
	#just name the files after the scene name
	#hope they weren't lazy there too.
	if filename=="model":
		filename=scene.getName()
		#basefilename and /path/filename are the same
		bfilename=filename
	else:
		#extract the actual filename from the
		#entire path of the /path/filename
		bfilename = os.path.basename(filename)

	exported_materials = []

	#fill the chunks full of data
	primary=primary_chunk()
	#get all the objects in this scene
	object_list=Blender.Object.Get()
	#fill up the data structures with objects
	for obj in object_list:
		#create a new object chunk
		if obj.getType()=="Mesh":
			primary.obj_info.obj_chunks.append(object_chunk())
			#get the mesh data
			blender_mesh=obj.getData()
			#set the object name
			primary.obj_info.obj_chunks[len(primary.obj_info.obj_chunks)-1].name=obj.getName()

			matrix = obj.getMatrix()

			#make a new mesh chunk object
			mesh=mesh_chunk()

			#fill verts in
			vert_count=0
			vert_list={}
			for vert in blender_mesh.verts:
				# get the vertex and apply all transforms to it
				v = point_by_matrix(vert.co, matrix)
				vert_key=(v[0], v[1], v[2])
				mesh.v_chunk.verts.append(vert_key)
				if not vert_list.has_key(vert_key):
					#print "didn't have the key"
					vert_list[vert_key] = vert_count  #save this info for the face lookup table
					vert_count = vert_count + 1

			print "vert_count: ", vert_count
			print "mesh.verts: ", len(blender_mesh.verts)

			for m in blender_mesh.materials:
				mesh.f_chunk.m_chunks.append(obj_material_chunk())
				mesh.f_chunk.m_chunks[len(mesh.f_chunk.m_chunks)-1].name = m.name

				# materials should only be exported once
				if m.name in exported_materials:
					continue

				material = material_chunk()
				material.matname_chunk.name=m.name
				material.matambient_chunk.col1.col = m.mirCol
				material.matambient_chunk.col2.col = m.mirCol
				material.matdiffuse_chunk.col1.col = m.rgbCol
				material.matdiffuse_chunk.col2.col = m.rgbCol
				material.matspecular_chunk.col1.col = m.specCol
				material.matspecular_chunk.col2.col = m.specCol

				primary.obj_info.mat_chunks.append(material)

				exported_materials.append(m.name)


			facenr=0
			#fill in faces
			for face in blender_mesh.faces:
				#is this a tri or a quad
				num_fv=len(face.v)
				
				#it's a tri
				if num_fv==3:
					indexes=[0]*3
					for i in range (0,3):
						#build the keys
						# get the vertex and apply all transforms to it
						vert=point_by_matrix(face.v[i].co, matrix)
						vert_key=(vert[0], vert[1], vert[2])
						#look up the key to get the index
						vert_index=vert_list[vert_key]
						indexes[i]=vert_index
					mesh.f_chunk.faces.append(indexes)
					if (face.materialIndex < len(mesh.f_chunk.m_chunks)):
						mesh.f_chunk.m_chunks[face.materialIndex].faces.append(facenr)
					facenr+=1
					
				#it's a quad
				elif num_fv==4:
					indexes=[0]*4
					for i in range (0,4):
						#build the keys
						vert=point_by_matrix(face.v[i].co, matrix)
						vert_key=(vert[0], vert[1], vert[2])
						#look up the key to get the index
						vert_index=vert_list[vert_key]
						indexes[i]=vert_index
						first_tri=(indexes[0], indexes[1], indexes[2])
						sec_tri=(indexes[2], indexes[3], indexes[0])
						
					mesh.f_chunk.faces.append(first_tri)  # 0,1,2
					mesh.f_chunk.faces.append(sec_tri)  # 2,3,0
					#first tri
					if (face.materialIndex < len(mesh.f_chunk.m_chunks)):
						mesh.f_chunk.m_chunks[face.materialIndex].faces.append(facenr)
					facenr+=1
					#other tri
					if (face.materialIndex < len(mesh.f_chunk.m_chunks)):
						mesh.f_chunk.m_chunks[face.materialIndex].faces.append(facenr)
					facenr+=1


			#fill in the UV info
			if blender_mesh.hasVertexUV():
				for vert in blender_mesh.verts:
					uv_coord=(vert.uvco)
					mesh.uv_chunk.uv.append((uv_coord[0], uv_coord[1]))

			elif blender_mesh.hasFaceUV():
				for face in blender_mesh.faces:
					num_fv=len(face.v)
					if num_fv==3:
						for i in range (0,3):
							uv_coord=(face.uv[i])
							mesh.uv_chunk.uv.append((uv_coord[0], uv_coord[1]))
					elif num_fv==4:
						for i in range (0,4):
							uv_coord=(face.uv[i])
							mesh.uv_chunk.uv.append((uv_coord[0], uv_coord[1]))


			#filled in our mesh, lets add it to the file
			primary.obj_info.obj_chunks[len(primary.obj_info.obj_chunks)-1].mesh_chunks.append(mesh)

	#check the size
	primary.get_size()
	#open the files up for writing
	file = open( filename, "wb" )
	#recursively write the stuff to file
	primary.write(file)
	file.close()

#***********************************************
# MAIN
#***********************************************

	
def my_callback(filename):
	if(filename.find('.3ds', -4) <= 0):
		filename += '.3ds'
	save_3ds(filename)

Blender.Window.FileSelector(my_callback, "Export 3DS")