After some requests, I re-posting, the 3DS_importer by Federico B.
Please Up-grade the script if you understand, python.
The Home page of the author
#!BPY
""" Registration info for Blender menus:
Name: '3DS'
Blender: 232
Group: 'Import'
Tip: 'Export to 3DS (.3ds) format.'
"""
#//////////////////////////////////// IMPORT3DS /////////////////////////////////*
#/////
#///// SCRIPT: IMPORT3DS.PY
#///// VERSION: 0.9
#/////
#///// AUTHOR: Federico B. 2003 - [email protected]
#///// WEB: http://thera.teppisti.it
#/////
#///// Thanks to GameTutorials.com for the 42 tutorial and to its author
#///// (wich this work is based on) and to http://web.hypersurf.com/~sully/OpenGL/DemoBox.html.
#///// Thanks to Reevan Mckay for his scritp full of usefull info.
#///// And especially, thanks to Blender.it and his comunity for his support and help
#/////
#///// REQUIRMENTS: Python full istallation (www.python.org) and Blender
#/////
#///// DONE: The Script now import 3d mesh, material, textures and textures coords,
#///// light and camera supp. The standard out is logged in a file blende_out.txt
#///// in the blender dir. Now it's possible insert the script in the blender script
#///// dir to use automatic load up in blender import menu.
#/////
#///// TODO: Finish light and add keyframe support. For now texture are loaded but not
#///// correctly assigned to the mesh faces
#/////
#///// LICENSE: The script is under GPL and feel free to use it but remember that
#///// 3ds file format is covered by its own license.
#/////
#//////////////////////////////////// IMPORT3DS /////////////////////////////////*
import array
import struct
#import string
#from string import *
import sys
import exceptions
import Blender
from Blender import NMesh, Material, Texture, Scene, Lamp, Image
from Blender.Draw import *
from Blender.BGL import *
import math
from math import sin, cos, sqrt
from math import cos, acos, sin, asin
from math import pi as PI
import exceptions
from exceptions import *
#>------ Primary CChunk, at the beginning of each file
PRIMARY = long("0x4D4D",16);
#>------ Main CChunks
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= long("0xAFFF",16); #This stored the texture info
OBJECT = long("0x4000",16); #This stores the Faces, vertices, etc...
#>------ sub defines of MATERIAL
MATNAME = long("0xA000",16); # This holds the material name
MATAMBIENT = long("0xA010",16); # Ambient color of the object/material
MATDIFFUSE = long("0xA020",16); # This holds the color of the object/material
MATSPECULAR = long("0xA030",16); # SPecular color of the object/material
MATSHINESS = long("0xA040",16); # ??
MATMAP = long("0xA200",16); # This is a header for a new material
MATMAPFILE = long("0xA300",16); # This holds the file name of the texture
#>------ sub defines of OBJECT
OBJECT_MESH = long("0x4100",16); # This lets us know that we are reading a new object
OBJECT_LIGHT = long("0x4600",16); # This lets un know we are reading a light object
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
#//////////////////////////////////// C3DMODEL /////////////////////////////////*
#/////
#///// This class holds the data of an intere model
#/////
#//////////////////////////////////// C3DMODEL /////////////////////////////////*
class C3DModel(object):
numOfObjects = 0; # The number of objects in the model
numOfMaterials = 0; # The number of materials for the model
numOfLights = 0;
numOfCameras = 0;
pMaterials = []; # The list of material information (Textures and colors)
pObjects = []; # The object list for our model
pLights = []
pCameras = [];
#//////////////////////////////////// C3DOBJECT /////////////////////////////////*
#/////
#///// This class holds the data of a part of a model
#/////
#//////////////////////////////////// C3DOBJECT /////////////////////////////////*
class C3DObject(object):
numOfVerts = 0; # The number of verts in the model
numOfFaces = 0; # The number of faces in the model
numTexVertex= 0; # The number of texture coordinates
materialID = []; # The textures ID to use, which is the index into our texture array
bHasTexture = False; # This is TRUE if there is a texture map for this object
strName = ""; # The name of the object
pVerts = []; # The object's vertices
pNormals = []; # The object's normals
pTexVerts = []; # The texture's UV coordinates
pFaces = []; # The faces information of the object
#//////////////////////////////////// CINDICES /////////////////////////////////*
#/////
#///// This class holds the indeces of a face
#/////
#//////////////////////////////////// CINDICES /////////////////////////////////*
class CIndices(object):
a = 0;
b = 0;
c = 0;
bVisible = 0; # This will hold point1, 2, and 3 index's into the vertex array plus a visible flag
#//////////////////////////////////// CFACE /////////////////////////////////*
#/////
#///// This class colde the data of a face
#/////
#//////////////////////////////////// CFACE /////////////////////////////////*
class CFace(object):
vertIndex = CIndices(); # indicies for the verts that make up this triangle
coordIndex = CIndices(); # indicies for the tex coords to texture this face
#//////////////////////////////////// CMATERIALINFO /////////////////////////////////*
#/////
#///// This class holds materilas and texture info
#/////
#//////////////////////////////////// CMATERIALINFO /////////////////////////////////*
class CMaterialInfo(object):
strName = ""; # The texture name
strFile = ""; # The texture file name (If this is set it's a texture map)
color = []; # The color of the object (R, G, B)
ambient = []; # The ambient color
specular = []; # The speculat color
numOfFaces = 0; # number of Faces covered by the material
pFaces = []; # Array of Faces covered by the material
#//////////////////////////////////// CLIGHT /////////////////////////////////*
#/////
#///// This class holds materilas and texture info
#/////
#//////////////////////////////////// CLIGHT /////////////////////////////////*
class CLight(object):
position = [];
color = [];
#//////////////////////////////////// CLIGHT /////////////////////////////////*
#/////
#///// This class holds materilas and texture info
#/////
#//////////////////////////////////// CLIGHT /////////////////////////////////*
class CCamera(object):
Position = [];
Target = [];
Angle = 0.0;
Focus = 0.0;
near = 0.0;
far = 0.0;
#//////////////////////////////////// CCHUNK /////////////////////////////////*
#/////
#///// This class is the basico unit of 3ds subdivision
#/////
#//////////////////////////////////// CCHUNK /////////////////////////////////*
class CChunk(object):
ID = 0; # The CChunk's ID
length = 0; # The length of the CChunk
bytesRead = 0; # The amount of bytes read within that CChunk
#//////////////////////////////////// CLOAD3DS /////////////////////////////////*
#/////
#///// #This class load 3ds info and construct a 3d model from it
#/////
#//////////////////////////////////// CLOAD3DS /////////////////////////////////*
class CLoad3ds(object):
#///////////////////////////////// __INIT__ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
#/////
#///// Constructor of the class
#/////
#///////////////////////////////// __INIT__ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
def __init__(self):
self.m_FilePointer = [];
self.currentChunk = CChunk();
self.tempChunk = CChunk();
#///////////////////////////////// FREAD \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
#/////
#///// This function emulate C++ fread
#/////
#///////////////////////////////// FREAD \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
def fread( self, Type, Size, Count ):
try:
value = ( struct.unpack( "%s%s" % (Count,Type), self.m_FilePointer.read(Size)));
except IOError:
print("IO Error, failed to read the next byte in the stream.");
return (Size*Count, value[0])
#///////////////////////////////// SKIP \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
#/////
#///// This function permit to skip some bytes
#/////
#///////////////////////////////// SKIP \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
def skip(self, n):
try:
self.m_FilePointer.read(n)
except IOError:
print("IO Error, failed to read the next byte in the stream.");
return n;
#///////////////////////////////// IMPORT3DS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
#/////
#///// Main function of the class, open file and import the 3ds into our
#///// C3DModel class
#/////
#///////////////////////////////// IMPORT3DS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
def Import3DS( self, pModel, strFileName ):
self.currentChunk = CChunk();
# Open the 3DS file
try:
self.m_FilePointer = open(strFileName, "rb");
except IOError:
# Make sure we have a valid file pointer (we found the file)
print("Unable to find the file %s!" % (strFileName));
return;
# Once we have the file open, we need to read the very first data chunk
# to see if it's a 3DS file. That way we don't read an invalid file.
# If it is a 3DS file, then the first chunk ID will be equal to PRIMARY (some hex num)
# Read the first chuck of the file to see if it's a 3DS file
self.ReadChunk(self.currentChunk);
# Make sure this is a 3DS file
if (self.currentChunk.ID != PRIMARY):
print("Unable to load PRIMARY chuck from file: %s!" % (strFileName));
print "%s" % self.currentChunk.ID
return;
# Now we actually start reading in the data. ProcessNextChunk() is recursive
# Begin loading objects, by calling this recursive function
self.ProcessNextChunk(pModel, self.currentChunk);
# After we have read the whole 3DS file, we want to calculate our own vertex normals.
# self.ComputeNormals(pModel);
# Clean up after everything
self.CleanUp();
return;
#///////////////////////////////// CLEAN UP \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
#/////
#///// This function cleans up our allocated memory and closes the file
#/////
#///////////////////////////////// CLEAN UP \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
def CleanUp( self ):
self.m_FilePointer.close(); # Close the current file pointer
#/////////////////////////// PROCESS NEXT CHUNK \\\\\\\\\\\\\\\\\\\\\\\\\\\*
#/////
#///// This function reads the main sections of the .3DS file, then dives
#///// deeper with recursion
#/////
#/////////////////////////// PROCESS NEXT CHUNK \\\\\\\\\\\\\\\\\\\\\\\\\\\*
def ProcessNextChunk( self, pModel, pPreviousChunk ):
print "CHUNK <--"
self.currentChunk = CChunk(); # The current chunk to load
version = 0; # This will hold the file version
# Below we check our chunk ID each time we read a new chunk. Then, if
# we want to extract the information from that chunk, we do so.
# If we don't want a chunk, we just read past it.
# Continue to read the sub chunks until we have reached the length.
# After we read ANYTHING we add the bytes read to the chunk and then check
# check against the length.
while pPreviousChunk.bytesRead < pPreviousChunk.length:
# Read next Chunk
self.ReadChunk(self.currentChunk);
# Check the chunk ID
if self.currentChunk.ID == VERSION: # This holds the version of the file
# If the file was made in 3D Studio Max, this chunk has an int that
# holds the file version. Since there might be new additions to the 3DS file
# format in 4.0, we give a warning to that problem.
# However, if the file wasn't made by 3D Studio Max, we don't 100% what the
# version length will be so we'll simply ignore the value
# Read the file version and add the bytes read to our bytesRead variable
value = self.fread("H", 2, 1);
self.currentChunk.bytesRead += value[0]; version = value[1];
print "VERSION %s, file version %s" % (VERSION,version)
self.currentChunk.bytesRead += self.skip(self.currentChunk.length - self.currentChunk.bytesRead);
# If the file version is over 3, give a warning that there could be a problem
if (version > int("0x03",16)):
print( "This 3DS file is over version 3 so it may load incorrectly");
elif self.currentChunk.ID == OBJECTINFO: # This holds the version of the mesh
# This chunk holds the version of the mesh. It is also the head of the MATERIAL
# and OBJECT chunks. From here on we start reading in the material and object info.
# Read the next chunk
self.ReadChunk(self.tempChunk);
# Get the version of the mesh
value = self.fread("H", 2, 1);
self.tempChunk.bytesRead += value[0]; version = value[1]
print "OBJECTINFO %s, mesh version %s" % (OBJECTINFO,version)
# skip some trash
self.tempChunk.bytesRead += self.skip(self.tempChunk.length - self.tempChunk.bytesRead);
# Increase the bytesRead by the bytes read from the last chunk
self.currentChunk.bytesRead += self.tempChunk.bytesRead;
# Go to the next chunk, which is the object has a texture, it should be MATERIAL, then OBJECT.
self.ProcessNextChunk(pModel, self.currentChunk);
elif self.currentChunk.ID == MATERIAL: # This holds the material information
print "MATERIAL"
# This chunk is the header for the material info chunks
# Increase the number of materials
pModel.numOfMaterials += 1;
# This is used to add to our material list
newTexture = CMaterialInfo();
# Add a empty texture structure to our texture list.
# If you are unfamiliar with STL's "vector" class, all push_back()
# does is add a new node onto the list. I used the vector class
# so I didn't need to write my own link list functions.
pModel.pMaterials.append(newTexture);
# Proceed to the material loading function
self.ProcessNextMaterialChunk(pModel, self.currentChunk);
elif self.currentChunk.ID == OBJECT: # This holds the name of the object being read
print "OBJECT"
# This chunk is the header for the object info chunks. It also
# holds the name of the object.
# Increase the object count
pModel.numOfObjects += 1;
# A new object to add to our object list
newObject = C3DObject();
# Get the name of the object and store it, then add the read bytes to our byte counter.
value = self.GetString();
self.currentChunk.bytesRead += value[0]; newObject.strName = value[1];
# Add a new CObject node to our list of objects (like a link list)
pModel.pObjects.append(newObject);
print "OBJECT %s, object name %s" % (OBJECT,value[1])
# Now proceed to read in the rest of the object information
self.ProcessNextObjectChunk(pModel, newObject, self.currentChunk);
elif self.currentChunk.ID == EDITKEYFRAME:
print "EDITKEYFRAME"
# Because I wanted to make this a SIMPLE tutorial as possible, I did not include
# the key frame information. This chunk is the header for all the animation info.
# In a later tutorial this will be the subject and explained thoroughly.
#ProcessNextKeyFrameChunk(pModel, self.currentChunk);
# Read past this chunk and add the bytes read to the byte counter
self.currentChunk.bytesRead += self.skip(self.currentChunk.length - self.currentChunk.bytesRead);
else :
print "CHUNK ELSE at %s " % self.m_FilePointer.tell()
# If we didn't care about a chunk, then we get here. We still need
# to read past the unknown or ignored chunk and add the bytes read to the byte counter.
self.currentChunk.bytesRead += self.skip(self.currentChunk.length - self.currentChunk.bytesRead);
# Add the bytes read from the last chunk to the previous chunk passed in.
pPreviousChunk.bytesRead += self.currentChunk.bytesRead;
# Free the current CChunk and set it back to the previous CChunk (since it started that way)
self.currentChunk = pPreviousChunk;
#///////////////////////////////// PROCESS NEXT OBJECT CHUNK \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
#/////
#///// This function handles all the information about the objects in the file
#/////
#///////////////////////////////// PROCESS NEXT OBJECT CHUNK \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
def ProcessNextObjectChunk(self, pModel, pObject, pPreviousChunk):
print "--> OBJECT CHUNK <--"
# The current chunk to work with
self.currentChunk = CChunk();
# Continue to read these chunks until we read the end of this sub chunk
while pPreviousChunk.bytesRead < pPreviousChunk.length:
# Read the next chunk
self.ReadChunk(self.currentChunk);
# Check which chunk we just read
if self.currentChunk.ID == OBJECT_MESH: # This lets us know that we are reading a new object
print "--> OBJECT_MESH"
# We found a new object, so let's read in it's info using recursion
self.ProcessNextObjectChunk(pModel, pObject, self.currentChunk);
elif self.currentChunk.ID == OBJECT_LIGHT:
print "--> OBJECT_LIGHT"
# Increase the light count
pModel.numOfLights += 1;
newLight = CLight();
self.ReadLight(newLight, self.currentChunk)
pModel.pLights.append(newLight);
elif self.currentChunk.ID == OBJECT_CAMERA:
print "--> OBJECT_CAMERA"
pModel.numOfCameras += 1;
newCamera = CCamera();
pModel.pCameras.append(newCamera);
self.ReadCamera(newCamera, self.currentChunk);
elif self.currentChunk.ID == OBJECT_CAM_RANGES:
print "--> OBJECT_CAM_RANGES"
self.ReadCameraRanges(pModel, self.currentChunk);
elif self.currentChunk.ID == OBJECT_VERTICES: # This is the objects vertices
print "--> OBJECT_VERTICES"
self.ReadVertices(pObject, self.currentChunk);
elif self.currentChunk.ID == OBJECT_FACES: # This is the objects face information
print "--> OBJECT_FACES"
self.ReadVertexIndices(pObject, self.currentChunk);
elif self.currentChunk.ID == OBJECT_MATERIAL: # This holds the material name that the object has
print "--> OBJECT_MATERIAL"
# This chunk holds the name of the material that the object has assigned to it.
# This could either be just a color or a texture map. This chunk also holds
# the faces that the texture is assigned to (In the case that there is multiple
# textures assigned to one object, or it just has a texture on a part of the object.
# Since most of my game objects just have the texture around the whole object, and
# they aren't multitextured, I just want the material name.
# We now will read the name of the material assigned to this object
self.ReadObjectMaterial(pModel, pObject, self.currentChunk);
elif self.currentChunk.ID == OBJECT_UV: # This holds the UV texture coordinates for the object
print "--> OBJECT_UV"
# This chunk holds all of the UV coordinates for our object. Let's read them in.
self.ReadUVCoordinates(pObject, self.currentChunk);
else:
print "--> OBJECTCHUNK ELSE at %s " % self.m_FilePointer.tell()
# Read past the ignored or unknown chunks
self.currentChunk.bytesRead += self.skip(self.currentChunk.length - self.currentChunk.bytesRead);
# Add the bytes read from the last chunk to the previous chunk passed in.
pPreviousChunk.bytesRead += self.currentChunk.bytesRead;
# Free the current CChunk and set it back to the previous CChunk (since it started that way)
self.currentChunk = pPreviousChunk;
#///////////////////////////////// PROCESS NEXT MATERIAL CHUNK \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
#/////
#///// This function handles all the information about the material (Texture)
#/////
#///////////////////////////////// PROCESS NEXT MATERIAL CHUNK \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
def ProcessNextMaterialChunk(self, pModel, pPreviousChunk):
print "--> MATERIAL CHUNK <--";
# The current chunk to work with
self.currentChunk = CChunk();
# Continue to read these chunks until we read the end of this sub chunk
while pPreviousChunk.bytesRead < pPreviousChunk.length:
# Read the next chunk
self.ReadChunk(self.currentChunk);
# Check which chunk we just read in
if self.currentChunk.ID == MATNAME: # This chunk holds the name of the material
print "--> MATNAME"
# Here we read in the material name
value = self.GetString();
self.currentChunk.bytesRead += value[0]; pModel.pMaterials[pModel.numOfMaterials - 1].strName = value[1];
print "--> MATNAME %s, material name %s" % (MATNAME,value[1]);
elif self.currentChunk.ID == MATAMBIENT: # This holds the R G B color of our object
print "--> MATAMBIENT";
self.ReadAmbientChunk((pModel.pMaterials[pModel.numOfMaterials - 1]), self.currentChunk);
elif self.currentChunk.ID == MATDIFFUSE: # This holds the R G B color of our object
print "--> MATDIFFUSE";
self.ReadColorChunk((pModel.pMaterials[pModel.numOfMaterials - 1]), self.currentChunk);
elif self.currentChunk.ID == MATSPECULAR: # This holds the R G B color of our object
print "--> MATSPECULAR";
self.ReadSpecularChunk((pModel.pMaterials[pModel.numOfMaterials - 1]), self.currentChunk);
elif self.currentChunk.ID == MATSHINESS:
# ---> da inserire un campo apposta per il colore
print "--> MATSHINESS";
self.currentChunk.bytesRead += self.skip(self.currentChunk.length - self.currentChunk.bytesRead);
elif self.currentChunk.ID == MATMAP:
print "--> MATMAP";
# Proceed to read in the material information
self.ProcessNextMaterialChunk(pModel, self.currentChunk);
elif self.currentChunk.ID == MATMAPFILE: # This stores the file name of the material
print "--> MATMAPFILE"
# Here we read in the material's file name
value = self.GetString();
self.currentChunk.bytesRead += value[0]; pModel.pMaterials[pModel.numOfMaterials - 1].strFile = value[1];
print "--> MATMAPFILE %s, material file name %s" % (MATMAPFILE,value[1]);
else:
print "--> MATERIAL ELSE at %s " % self.m_FilePointer.tell();
# Read past the ignored or unknown chunks
self.currentChunk.bytesRead += self.skip(self.currentChunk.length - self.currentChunk.bytesRead);
# Add the bytes read from the last chunk to the previous chunk passed in.
pPreviousChunk.bytesRead += self.currentChunk.bytesRead;
# Free the current chunk and set it back to the previous chunk (since it started that way)
self.currentChunk = pPreviousChunk;
#///////////////////////////////// READ CHUNK \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
#/////
#///// This function reads in a chunk ID and it's length in bytes
#/////
#///////////////////////////////// READ CHUNK \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
def ReadChunk(self, pChunk):
# This reads the chunk ID which is 2 bytes.
# The chunk ID is like OBJECT or MATERIAL. It tells what data is
# able to be read in within the chunks section.
value = self.fread( "H", 2, 1 );
pChunk.bytesRead = value[0]; pChunk.ID = value[1];
# Then, we read the length of the chunk which is 4 bytes.
# This is how we know how much to read in, or read past.
value = self.fread( "L", 4, 1 );
pChunk.bytesRead += value[0]; pChunk.length = value[1];
#///////////////////////////////// GET STRING \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
#/////
#///// This function reads in a string of characters
#/////
#///////////////////////////////// GET STRING \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
def GetString(self):
string=""
while (1):
char=self.m_FilePointer.read(1)
if (char=='\0'):
return ( (len(string)+1), string)
else:
string=string+char
#///////////////////////////////// READ LIGHT \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
#/////
#///// This function reads in the Light chunk
#/////
#///////////////////////////////// READ LIGHT \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
def ReadLight(self, pLight, pChunk):
print "-->READLIGHT"
# Read the Position for the light
value = self.fread("f",4,1);
pChunk.bytesRead += value[0]; pLight.position.append(value[1])
value = self.fread("f",4,1);
pChunk.bytesRead += value[0]; pLight.position.append(value[1])
value = self.fread("f",4,1);
pChunk.bytesRead += value[0]; pLight.position.append(value[1])
#///////////////////////////////// READ COLOR \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
#/////
#///// This function reads in the RGB color data
#/////
#///////////////////////////////// READ COLOR \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
def ReadColorChunk(self, pMaterial, pChunk):
# Read the color chunk info
self.ReadChunk(self.tempChunk);
# Read in the R color (3 bytes - 0 through 255)
value = self.fread("B",1,1);
self.tempChunk.bytesRead += 1; pMaterial.color.append(value[1]);
value = self.fread("B",1,1);
self.tempChunk.bytesRead += 1; pMaterial.color.append(value[1]);
value = self.fread("B",1,1);
self.tempChunk.bytesRead += 1; pMaterial.color.append(value[1]);
# Add the bytes read to our chunk
pChunk.bytesRead += self.tempChunk.bytesRead;
#///////////////////////////////// READ AMBIENT \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
#/////
#///// This function reads in the RGB color data
#/////
#///////////////////////////////// READ AMBIENT \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
def ReadAmbientChunk(self, pMaterial, pChunk):
# Read the color chunk info
self.ReadChunk(self.tempChunk);
# Read in the R color (3 bytes - 0 through 255)
value = self.fread("B",1,1);
self.tempChunk.bytesRead += 1; pMaterial.ambient.append(value[1]);
value = self.fread("B",1,1);
self.tempChunk.bytesRead += 1; pMaterial.ambient.append(value[1]);
value = self.fread("B",1,1);
self.tempChunk.bytesRead += 1; pMaterial.ambient.append(value[1]);
# Add the bytes read to our chunk
pChunk.bytesRead += self.tempChunk.bytesRead;
#///////////////////////////////// READ SPECULAR \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
#/////
#///// This function reads in the RGB color data
#/////
#///////////////////////////////// READ SPECULAR \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
def ReadSpecularChunk(self, pMaterial, pChunk):
# Read the color chunk info
self.ReadChunk(self.tempChunk);
# Read in the R color (3 bytes - 0 through 255)
value = self.fread("B",1,1);
self.tempChunk.bytesRead += 1; pMaterial.specular.append(value[1]);
value = self.fread("B",1,1);
self.tempChunk.bytesRead += 1; pMaterial.specular.append(value[1]);
value = self.fread("B",1,1);
self.tempChunk.bytesRead += 1; pMaterial.specular.append(value[1]);
# Add the bytes read to our chunk
pChunk.bytesRead += self.tempChunk.bytesRead;
#///////////////////////////////// READ CAMERA \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
#/////
#///// This function reads in the camera data
#/////
#///////////////////////////////// READ CAMERA \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
def ReadCamera(self, pCamera, pChunk):
print "--> READCAMERA";
# camera x
value = self.fread("f", 4, 1);
pChunk.bytesRead += value[0]; pCamera.Position.append(value[1]);
# camera y
value = self.fread("f", 4, 1);
pChunk.bytesRead += value[0]; pCamera.Position.append(value[1]);
# camera z
value = self.fread("f", 4, 1);
pChunk.bytesRead += value[0]; pCamera.Position.append(value[1]);
# tartget x
value = self.fread("f", 4, 1);
pChunk.bytesRead += value[0]; pCamera.Target.append(value[1]);
# target y
value = self.fread("f", 4, 1);
pChunk.bytesRead += value[0]; pCamera.Target.append(value[1]);
# target z
value = self.fread("f", 4, 1);
pChunk.bytesRead += value[0]; pCamera.Target.append(value[1]);
# bank angle
value = self.fread("f", 4, 1);
pChunk.bytesRead += value[0]; pCamera.Angle = value[1];
# focus
value = self.fread("f", 4, 1);
pChunk.bytesRead += value[0]; pCamera.Focus = value[1];
#///////////////////////////////// READ CAMERA RANGES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
#/////
#///// This function reads in the camra ranges data
#/////
#///////////////////////////////// READ CAMERA RANGES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
def ReadCameraRanges(self,pModel, pChunk):
# camera near range
value = self.fread("f", 4, 1);
pChunk.bytesRead += value[0];
(pModel.pCameras[len(pModel.pCameras)-1]).near = value[1];
# camera far range
value = self.fread("f", 4, 1);
pChunk.bytesRead += value[0];
(pModel.pCameras[len(pModel.pCameras)-1]).far = value[1];
#///////////////////////////////// READ VERTEX INDECES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
#/////
#///// This function reads in the indices for the vertex array
#/////
#///////////////////////////////// READ VERTEX INDECES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
def ReadVertexIndices(self, pObject, pPreviousChunk):
print "--> READVERTEXINDICES"
# In order to read in the vertex indices for the object, we need to first
# read in the number of them, then read them in. Remember,
# we only want 3 of the 4 values read in for each face. The fourth is
# a visibility flag for 3D Studio Max that doesn't mean anything to us.
# Read in the number of faces that are in this object (int)
value = self.fread("H",2,1);
pPreviousChunk.bytesRead += value[0]; pObject.numOfFaces = value[1];
# Alloc enough memory for the faces and initialize the structure
pObject.pFaces = [];
# Go through all of the faces in this object
for i in xrange(pObject.numOfFaces):
# Read the first vertice index for the current face
value = self.fread("H",2,1);
pPreviousChunk.bytesRead += value[0]; a = value[1]
# Read the second vertice index for the current face
value = self.fread("H",2,1);
pPreviousChunk.bytesRead += value[0]; b = value[1]
# Read the third vertice index for the current face
value = self.fread("H",2,1);
pPreviousChunk.bytesRead += value[0]; c = value[1]
# Visibility flag
value = self.fread("H",2,1);
pPreviousChunk.bytesRead += value[0]; bVisible = value[1]
# Store the index in our face structure.
indices = CIndices();
indices.a = a; indices.b = b; indices.c = c; indices.bVisible = bVisible;
face = CFace();
face.vertIndex = indices;
pObject.pFaces.append(face);
#///////////////////////////////// READ UV COORDINATES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
#/////
#///// This function reads in the UV coordinates for the object
#/////
#///////////////////////////////// READ UV COORDINATES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
def ReadUVCoordinates(self, pObject, pPreviousChunk):
print "--> READUVCOORDINATES"
# In order to read in the UV indices for the object, we need to first
# read in the amount there are, then read them in.
# Read in the number of UV coordinates there are (int)
value = self.fread("H", 2, 1);
pPreviousChunk.bytesRead += value[0]; pObject.numTexVertex = value[1]
# Allocate memory to hold the UV coordinates
pObject.pTexVerts = []
# Read in the texture coodinates (an array 2 float)
for i in xrange(pObject.numTexVertex):
value = self.fread("f", 4, 1);
pPreviousChunk.bytesRead += value[0]; vert1 = value[1]
value = self.fread("f", 4, 1);
pPreviousChunk.bytesRead += value[0]; vert2 = value[1]
TexVector = CVector2(vert1,vert2);
pObject.pTexVerts.append( TexVector );
#///////////////////////////////// READ VERTICES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
#/////
#///// This function reads in the vertices for the object
#/////
#///////////////////////////////// READ VERTICES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
def ReadVertices(self, pObject, pPreviousChunk):
print "--> READVERTICES"
# Like most chunks, before we read in the actual vertices, we need
# to find out how many there are to read in. Once we have that number
# we then self.fread() them into our vertice array.
# Read in the number of vertices (int)
value = self.fread("H",2,1);
pPreviousChunk.bytesRead += value[0]; pObject.numOfVerts = value[1];
pObject.pVerts = [];
for i in xrange(pObject.numOfVerts):
# Read in the vertices
value = self.fread("f",4,1);
pPreviousChunk.bytesRead += value[0]; vert1 = value[1];
value = self.fread("f",4,1);
pPreviousChunk.bytesRead += value[0]; vert2 = value[1];
value = self.fread("f",4,1);
pPreviousChunk.bytesRead += value[0]; vert3 = value[1];
VertVector = CVector3(vert1,vert2,vert3);
pObject.pVerts.append( VertVector );
# Now we should have all of the vertices read in. Because 3D Studio Max
# Models with the Z-Axis pointing up (strange and ugly I know!), we need
# to flip the y values with the z values in our vertices. That way it
# will be normal, with Y pointing up. If you prefer to work with Z pointing
# up, then just delete this next loop. Also, because we swap the Y and Z
# we need to negate the Z to make it come out correctly.
# Go through all of the vertices that we just read and swap the Y and Z values
for i in xrange(pObject.numOfVerts):
# Store off the Y value
fTempY = pObject.pVerts[i].y;
# Set the Y value to the Z value
pObject.pVerts[i].y = pObject.pVerts[i].z;
# Set the Z value to the Y value,
# but negative Z because 3D Studio max does the opposite.
pObject.pVerts[i].z = -fTempY;
#///////////////////////////////// READ OBJECT MATERIAL \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
#/////
#///// This function reads in the material name assigned to the object and sets the materialID
#/////
#///////////////////////////////// READ OBJECT MATERIAL \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
def ReadObjectMaterial(self, pModel, pObject, pPreviousChunk):
print"--> READOBJECTMATERIAL"
strMaterial = ""; # This is used to hold the objects material name
# *What is a material?* - A material is either the color or the texture map of the object.
# It can also hold other information like the brightness, shine, etc... Stuff we don't
# really care about. We just want the color, or the texture map file name really.
# Here we read the material name that is assigned to the current object.
# strMaterial should now have a string of the material name, like "Material #2" etc..
value = self.GetString();
print "--> %s " % value[1]
pPreviousChunk.bytesRead += value[0]; strMaterial = value[1]
nFaces = 0; #This is used to hold the number of faces for the material
pFaces = []; #The array of faces
# Look if our material is applied to some faces rather than the whole object
value = self.fread("H",2,1);
pPreviousChunk.bytesRead += value[0]; nFaces = value[1];
if( nFaces > 0):
# Loop nFaces times to read all the face indexes
for i in xrange(nFaces):
value = self.fread("H",2,1);
pPreviousChunk.bytesRead += value[0];
pFaces.append(value[1]);
else:
nFaces = 0;
# Now that we have a material name, we need to go through all of the materials
# and check the name against each material. When we find a material in our material
# list that matches this name we just read in, then we assign the materialID
# of the object to that material index. You will notice that we passed in the
# model to this function. This is because we need the number of textures.
# Yes though, we could have just passed in the model and not the object too.
# Go through all of the textures
for i in range(pModel.numOfMaterials):
# If the material we just read in matches the current texture name
if strMaterial == pModel.pMaterials[i].strName:
# Add to the material ids list of this object the current index 'i'
pObject.materialID.append(i);
# Assing to our material the numbers of faces is applied to and it's face array
pModel.pMaterials[i].numOfFaces = nFaces;
if( nFaces > 0):
# Add to the material a face array of face index
pModel.pMaterials[i].pFaces = pFaces;
# Now that we found the material, check if it's a texture map.
# If the strFile has a string length of 1 and over it's a texture
if len(pModel.pMaterials[i].strFile) > 0:
# Set the object's flag to say it has a texture map to bind.
pObject.bHasTexture = True;
break;
# Read past the rest of the chunk since we don't care about shared vertices
# You will notice we subtract the bytes already read in this chunk from the total length.
pPreviousChunk.bytesRead += self.skip(pPreviousChunk.length - pPreviousChunk.bytesRead);
#/////////////////////////////////// CVECTOR3 ///////////////////////////////////*
#/////
#///// This is the vector class
#/////
#/////////////////////////////////// CVECTOR ////////////////////////////////////*
class CVector3(object):
def __init__(self,x,y,z):
self.x = x
self.y = y
self.z = z
#/////////////////////////////////// CVECTOR2 ///////////////////////////////////*
#/////
#///// This is the vector class
#/////
#/////////////////////////////////// CVECTOR2 ///////////////////////////////////*
class CVector2(object):
def __init__(self,x,y):
self.x = x
self.y = y
EVENT_PATHCHANGE= 1;
EVENT_IMPORT= 2;
EVENT_IMPORT_MATS= 3;
EVENT_QUIT= 4;
bMaterials = False
#//////////////////////////////////// TOBLENDER /////////////////////////////////*
#/////
#///// Import the info held by Model3d to blender
#/////
#//////////////////////////////////// TOBLENDER /////////////////////////////////*
def ToBlender(Model3d,FileName):
FilePath = FileName[0:(len(FileName)-len(Blender.sys.basename(FileName)))];
#Get out the current scene
scene = Scene.getCurrent()
#//////////////////////// LOAD ANY LIGHT \\\\\\\\\\\\\\\\\\\\\\\\
for i in xrange(Model3d.numOfLights):
lite = Blender.Lamp.New('Lamp');
# I cant fogur out where light color is stored :(
#lite.col( Model3d.pLights[i].color[0],
# Model3d.pLights[i].color[1],
# Model3d.pLights[i].color[2]);
lamp = Blender.Object.New('Lamp');
lamp.setLocation( Model3d.pLights[i].position[0],
Model3d.pLights[i].position[1],
Model3d.pLights[i].position[2]);
# Make the lamp a real light object
lamp.link(lite);
# add the lamp to the scene
scene.link(lamp);
#//////////////////////// LOAD ANY CAMERA \\\\\\\\\\\\\\\\\\\\\\\\\
for i in xrange(Model3d.numOfCameras):
cam = Blender.Camera.New('ortho');
cam.setClipEnd(Model3d.pCameras[i].far);
cam.setClipStart(Model3d.pCameras[i].near);
camera = Blender.Object.New('Camera');
camera.setLocation( Model3d.pCameras[i].Position[0],
Model3d.pCameras[i].Position[1],
Model3d.pCameras[i].Position[2]);
# make the camera a real camera object
camera.link(cam);
# add camera to the scene
scene.link(camera)
#///////////////// LOAD ANY TEXTURE AND MATERIAL \\\\\\\\\\\\\\\\\\\
if bMaterials == True:
mats = []
for i in xrange(0,Model3d.numOfMaterials):
mat = Material.New(Model3d.pMaterials[i].strName);
# Add color material
mat.rgbCol =( float(Model3d.pMaterials[i].color[0]),
float(Model3d.pMaterials[i].color[1]),
float(Model3d.pMaterials[i].color[2]) );
# Add the specular color
mat.specCol =( float(Model3d.pMaterials[i].specular[0]),
float(Model3d.pMaterials[i].specular[1]),
float(Model3d.pMaterials[i].specular[2]) );
# Add the ambient color (i'm not very sure about this)
mat.mirCol =( float(Model3d.pMaterials[i].ambient[0]),
float(Model3d.pMaterials[i].ambient[1]),
float(Model3d.pMaterials[i].ambient[2]) );
# If the material has a texture load in the image
if (len(Model3d.pMaterials[i].strFile) > 0):
try:
imgName = str(FilePath+Model3d.pMaterials[i].strFile);
img = Image.Load(imgName);
tex = Blender.Texture.New();
tex.setType('Image');
tex.image = img
mat.setTexture(0, tex);
except:
print "failed to load %s texture" % (FilePath+Model3d.pMaterials[i].strFile);
mats.append(mat);
#/////////////////////// FOR EVERY OBJECT \\\\\\\\\\\\\\\\\\\\\\\\\
for i in xrange(0,Model3d.numOfObjects):
# Make sure we have valid objects just in case. (size() is in the vector class)
if(len(Model3d.pObjects) <= 0):
break;
# Get the current object that we are displaying
pObject = C3DObject();
pObject = Model3d.pObjects[i];
currMesh = Blender.NMesh.New()
#////////////////////// ADD VERTICES \\\\\\\\\\\\\\\\\\\\\\\\\
# Go through all of the vertices of the object and assign them
# to the current mesh
for j in xrange(0,pObject.numOfVerts):
vert = NMesh.Vert(pObject.pVerts[j].x,
pObject.pVerts[j].y,
pObject.pVerts[j].z);
currMesh.verts.append(vert)
#////////////// ADD MATERIALS AND TEXTURES \\\\\\\\\\\\\\\\\
if bMaterials == True:
# If Object has a texture Set the flag for the uv texture
if pObject.bHasTexture == True:
currMesh.hasFaceUV(1);
# Add Vertex uvco coordinates
for j in xrange(0,pObject.numTexVertex):
currMesh.verts[j].uvco = ( pObject.pTexVerts[j].x,pObject.pTexVerts[j].y );
# Add the materials to the mesh
for m in xrange(0,len(mats)):
for ids in xrange(0,len(pObject.materialID)):
if( mats[m].name == Model3d.pMaterials[pObject.materialID[ids]].strName):
print "Appling to mesh " + str(mats[m].name) + " material"
currMesh.materials.append(mats[m]);
#/////////////////////// ADD FACES \\\\\\\\\\\\\\\\\\\\\\\\\
# Loop again in faces to assign the face indeces
for j in xrange(0,pObject.numOfFaces):
face = NMesh.Face();
face.v.append (currMesh.verts[pObject.pFaces[j].vertIndex.a]);
face.v.append (currMesh.verts[pObject.pFaces[j].vertIndex.b]);
face.v.append (currMesh.verts[pObject.pFaces[j].vertIndex.c]);
currMesh.faces.append(face);
#////////////// FINALLY CREATE THE MESH \\\\\\\\\\\\\\\\\
# Done, unpdate and draw
NMesh.PutRaw(currMesh,str(pObject.strName),1)
Blender.Redraw();
#//////////////////////////////////// EXITGUI /////////////////////////////////*
#/////
#///// Exit form the gui
#/////
#//////////////////////////////////// EXITGUI /////////////////////////////////*
def ExitGUI ():
Exit()
#//////////////////////////////////// EVENTGUI /////////////////////////////////*
#/////
#///// Process events
#/////
#//////////////////////////////////// EVENTGUI /////////////////////////////////*
def EventGUI (event):
global bMaterials
if (event==EVENT_IMPORT):
bMaterials = False
Blender.Window.FileSelector(Import, "3DS Import")
elif (event==EVENT_IMPORT_MATS):
bMaterials = True
Blender.Window.FileSelector(Import, "3DS Import")
elif (event==EVENT_QUIT):
ExitGUI()
#//////////////////////////////////// DRAWGUI /////////////////////////////////*
#/////
#///// Draw the gui in blender
#/////
#//////////////////////////////////// DRAWGUI /////////////////////////////////*
def DrawGUI():
glClearColor(0.6,0.6,0.6,0)
glClear(GL_COLOR_BUFFER_BIT)
Button ("Import",EVENT_IMPORT,32,48,100,32)
Button ("Import with mats",EVENT_IMPORT_MATS,252,48,100,32)
Button ("Cancel",EVENT_QUIT,132,10,100,32)
#//////////////////////////////////// IMPORT /////////////////////////////////*
#/////
#///// Load the 3ds file, parse it and import everything in blender
#/////
#//////////////////////////////////// IMPORT /////////////////////////////////*
def Import(FileName):
LOGDIR = '.'
logfile = '3ds_blend_log.txt'
try:
st_f=open(LOGDIR + logfile, 'a+', 0)
except IOError:
try:
st_f = open(LOGDIR + logfile, 'w+', 0)
except IOError:
st_f = open(LOGDIR + logfile, 'w+', 0)
sys.stderr=sys.stdout=st_f
try:
Loader = CLoad3ds();
Model3d= C3DModel()
Loader.Import3DS(Model3d,FileName);
ToBlender(Model3d,FileName)
except IOError:
Draw()
print "error while loading/processing 3ds file make sure path is correct"
#//////////////////////////////////// REGISTERGUI /////////////////////////////////*
#/////
#/////
#/////
#//////////////////////////////////// REGISTERGUI /////////////////////////////////*
def RegisterGUI ():
Register (DrawGUI,None,EventGUI)
#//////////////////////////////////// MAIN SCRIPT /////////////////////////////////*
#/////
#///// Start the script
#/////
#//////////////////////////////////// MAIN SCRIPT /////////////////////////////////*
RegisterGUI()