Page MenuHome

import_ldraw.py

import_ldraw.py

import os
import bpy
import bmesh
import io
# TODO: add support for, CHROME, METAL, RUBBER, .... (Need material artist)
# TODO: add edge rendering, 2 options in import menu: add edges, add optional edges (Maybe do it myself, otherwise ask help, if possible)
# FIXME: blender will transform color values, to his color space, maybe better result (Ask on forum)
colorsDict = {}
cursor = bpy.context.scene.cursor_location
class ColorEntry:
def __init__(self, partColor, edgeColor):
self.partColor = partColor
self.edgeColor = edgeColor
def getPartColor(self):
return self.partColor
def getEdgeColor(self):
return self.edgeColor
class ColorSheet:
def __init__(self, sheetPath):
with open(sheetPath, 'r') as f:
for line in f:
if(not line.startswith("0 !COLOUR")):
continue
self.parse(line)
def parse(self, line):
s = line.split()
index = 3
succeeded = False
if(s[index] == 'CODE'):
index += 1
colorIndex = int(s[index])
index += 1
mat = bpy.data.materials.new(str(colorIndex))
if(s[index] == 'VALUE'):
index += 1
partColor = htmlColorToIntRGBArray(s[index])
index += 1
if(s[index] == 'EDGE'):
index += 1
edgeColor = htmlColorToIntRGBArray(s[index])
index += 1
if(index < len(s) and s[index] == 'ALPHA'):
index += 1
partColor[3] = int(s[index]) / 255
edgeColor[3] = int(s[index]) / 255
index += 1
if(partColor[3] < 1):
mat.alpha = partColor[3]
mat.use_transparency = True
if(index < len(s)):
if(s[index] == "LUMINANCE"):
index += 1
mat.emit = int(s[index]) / 255
index += 1
pass
if(index < len(s)):
if(s[index] == "CHROME"):
pass
elif(s[index] == "PEARLESCENT"):
pass
elif(s[index] == "METAL"):
pass
elif(s[index] == "RUBBER"):
pass
elif(s[index] == "MATERIAL"):
index += 1
if(s[index] == "SPECKLE"):
pass
elif(s[index] == "GLITTER"):
pass
else:
pass
succeeded = True
if(not succeeded):
raise Exception("Parsing of color config file failed.")
mat2 = colorsDict.get(str(colorIndex) + "_EDGE")
if(mat2 == None):
mat2 = bpy.data.materials.new(str(colorIndex) + "_EDGE")
mat2.diffuse_color = (edgeColor[0], edgeColor[1], edgeColor[2])
colorsDict[colorIndex] = ColorEntry(mat, mat2)
class LegoParser:
def __init__(self, modelPath, libraryPath, removeDoubles = False, scaleFactor = 0.1):
bpy.ops.object.select_all(action='DESELECT')
self.matrices = []
self.internalFiles = {}
self.paths = [None] * 2
self.groupName = []
self.removeDoubles = removeDoubles
self.scaleFactor = scaleFactor
self.paths[0] = libraryPath + os.sep + "p"
self.paths[1] = libraryPath + os.sep + "parts"
self.parse(modelPath, colorsDict[16])
bpy.ops.object.origin_set(type = 'ORIGIN_GEOMETRY')
def parse(self, filePath, currentColor, object=None, verts=None, faces=None, mats=None, matsIndex=None):
if(filePath == "light.dat"):
lamp_data = bpy.data.lamps.new(name=filePath, type="POINT")
lamp_object = bpy.data.objects.new(name=filePath, object_data=lamp_data)
bpy.context.scene.objects.link(lamp_object)
lamp_object.location = self.toOriginal((0, 0,0))
bpy.context.scene.objects.active = lamp_object
for groupName in self.groupName:
bpy.ops.object.group_link(group=groupName)
return
file, primitive = self.findFile(filePath)
if(not primitive):
mesh = bpy.data.meshes.new(os.path.basename(filePath))
object = bpy.data.objects.new(os.path.basename(filePath), mesh)
verts = []
faces = []
mats = []
matsIndex = []
if (file == None):
if filePath in self.internalFiles:
name = bpy.data.groups.new(filePath).name
self.groupName.append(name)
self.parseBrickFromBuffer(self.internalFiles[filePath], currentColor, object, verts, faces, mats, matsIndex)
self.groupName.remove(name)
else:
raise Exception("A file hasn't been found, " + filePath)
else:
self.parseBrickFromFile(file, currentColor, object, verts, faces, mats, matsIndex)
if(not primitive and len(verts) != 0):
bpy.context.scene.objects.link(object)
mesh.from_pydata(verts, [], faces)
mesh.update()
counter = 0
bpy.context.scene.objects.active = object
object.select = True
bpy.ops.object.mode_set(mode = 'EDIT')
bm = bmesh.from_edit_mesh(mesh)
for f in bm.faces:
f.material_index = matsIndex[counter]
counter += 1
if(self.removeDoubles):
bpy.ops.mesh.remove_doubles()
bpy.ops.object.mode_set(mode = 'OBJECT')
for groupName in self.groupName:
bpy.ops.object.group_link(group=groupName)
def findFile(self, filePath):
if(os.sep == "/"):
filePath = filePath.replace("\\", "/")
if(os.path.isfile(filePath)):
return (filePath, False)
for path in self.paths:
file = path + os.sep + filePath
if(os.path.isfile(file)):
if (self.paths[0] == path or filePath[:2] == "s" + os.sep):
return (file, True)
else:
return (file, False)
return None, None
def parseBrickFromBuffer(self, file, currentColor, object, verts, faces, mats, matsIndex):
for line in file.splitlines():
line = line.strip()
if(line == ""):
continue
data = line.split()
if(data[0] != "0"):
self.parseMesh(data, currentColor, object, verts, faces, mats, matsIndex)
def parseBrickFromFile(self, file, currentColor, object, verts, faces, mats, matsIndex):
with open(file, 'r') as f:
for line in f:
line = line.strip()
if (line == ""):
continue
data = line.split()
if (len(data) > 2 and data[0] == "0" and data[1] == "FILE"):
headFile = self.parseFile(file)
self.parse(headFile, currentColor)
break
elif (data[0] != "0"):
if(len(self.groupName) == 0):
name = bpy.data.groups.new("MAIN").name
self.groupName.append(name)
self.parseMesh(data, currentColor, object, verts, faces, mats, matsIndex)
def parseFile(self, file):
headFileName = None
currentFileName = None
currentFileContent = ""
with open(file, 'r') as f:
for line in f:
if(line == ""):
continue
data = line.split(None, 2)
if(len(data) == 3 and data[0] == "0" and data[1] == "FILE"):
if(currentFileName != None):
self.internalFiles[currentFileName] = currentFileContent
currentFileContent = ""
if(headFileName == None):
headFileName = os.path.basename(data[2])
currentFileName = os.path.basename(data[2])
else:
currentFileName = data[2].strip()
else:
currentFileContent += line
self.internalFiles[currentFileName] = currentFileContent
return headFileName
def parseMesh(self, data, currentColor, object, verts, faces, mats, matsIndex):
colorIndex = int(data[1])
if(colorIndex == 16 or colorIndex == 24):
colorEntry = currentColor
if(colorIndex == 24):
color = colorEntry.getEdgeColor()
else:
color = colorEntry.getPartColor()
else:
colorEntry = colorsDict.get(colorIndex)
if(colorEntry == None):
a = ((colorIndex >> 24) & 0xFF)
if(a == 2 or a == 3):
r = ((colorIndex >> 16) & 0xFF) / 255
g = ((colorIndex >> 8) & 0xFF) / 255
b = (colorIndex & 0xFF) / 255
mat = bpy.data.materials.new(str(colorIndex))
mat.diffuse_color = (r, g, b)
if(a == 3):
mat.alpha = 0.5
mat.use_transparency = True
colorEntry = ColorEntry(mat, mat)
colorsDict[colorIndex] = colorEntry
elif(a == 4 or a == 6):
mat = self.createNodeSetup(colorIndex, a)
colorEntry = ColorEntry(mat, mat)
colorsDict[colorIndex] = colorEntry
else:
print("Color not found, fallback to main color")
colorEntry = colorsDict[16]
color = colorEntry.getPartColor()
if(data[0] == "1"):
mm = (float(data[2]), float(data[3]), float(data[4]), float(data[5]), float(data[6]), float(data[7]), float(data[8]), float(data[9]), float(data[10]), float(data[11]), float(data[12]), float(data[13]))
self.matrices.append(mm)
self.parse(data[14], colorEntry, object, verts, faces, mats, matsIndex)
self.matrices.remove(mm)
elif(data[0] == "2"):
# line
pass
elif(data[0] == "3" or data[0] == "4"):
if color in mats:
matsIndex.append(mats.index(color))
else:
object.data.materials.append(color)
mats.append(color)
matsIndex.append(len(mats) - 1)
length = len(verts)
if(data[0] == "3"):
p1 = self.toOriginal((float(data[2]), float(data[3]), float(data[4])))
p2 = self.toOriginal((float(data[5]), float(data[6]), float(data[7])))
p3 = self.toOriginal((float(data[8]), float(data[9]), float(data[10])))
verts.extend((p1, p2, p3))
faces.append((length, length + 1, length + 2))
else:
p1 = self.toOriginal((float(data[2]), float(data[3]), float(data[4])))
p2 = self.toOriginal((float(data[5]), float(data[6]), float(data[7])))
p3 = self.toOriginal((float(data[8]), float(data[9]), float(data[10])))
p4 = self.toOriginal((float(data[11]), float(data[12]), float(data[13])))
verts.extend((p1, p2, p3, p4))
faces.append((length, length + 1, length + 2, length + 3))
elif(data[0] == "5"):
# optional line
pass
def toOriginal(self, coordinate):
x = coordinate[0]
y = coordinate[1]
z = coordinate[2]
if(len(self.matrices) != 0):
for matrix in reversed(self.matrices):
tempX = matrix[3] * x + matrix[4] * y + matrix[5] * z + matrix[0]
tempY = matrix[6] * x + matrix[7] * y + matrix[8] * z + matrix[1]
tempZ = matrix[9] * x + matrix[10] * y + matrix[11] * z + matrix[2]
x = tempX
y = tempY
z = tempZ
return (x * self.scaleFactor + cursor[0], z * self.scaleFactor + cursor[1], -y * self.scaleFactor + cursor[2])
def createNodeSetup(self, colorIndex, type):
r1 = ((colorIndex >> 20) & 0xF) * 17 / 255
g1 = ((colorIndex >> 16) & 0xF) * 17 / 255
b1 = ((colorIndex >> 12) & 0xF) * 17 / 255
r2 = ((colorIndex >> 8) & 0xF) * 17 / 255
g2 = ((colorIndex >> 4) & 0xF) * 17 / 255
b2 = (colorIndex & 0xF) * 17 / 255
mat = bpy.data.materials.new(str(colorIndex))
mat.use_nodes = True
if(type == 6):
alpha = 0.5
mat.use_transparency = True
else:
alpha = 1
tree = mat.node_tree
tree.nodes.clear()
color1_n = tree.nodes.new('ShaderNodeRGB')
color1_n.location = 0, 220
color1_n.outputs[0].default_value = (r1, g1, b1, 1)
color2_n = tree.nodes.new('ShaderNodeRGB')
color2_n.location = 0, 0
color2_n.outputs[0].default_value = (r2, g2, b2, 1)
mix_n = tree.nodes.new('ShaderNodeMixRGB')
mix_n.location = 220, 100
mat2 = bpy.data.materials.new(str(colorIndex) + '_node')
mat2.alpha = alpha
mat_n = tree.nodes.new('ShaderNodeMaterial')
mat_n.location = 440, 0
mat_n.material = mat2
output_n = tree.nodes.new('ShaderNodeOutput')
output_n.location = 660, 100
tree.links.new(color1_n.outputs[0], mix_n.inputs[1])
tree.links.new(color2_n.outputs[0], mix_n.inputs[2])
tree.links.new(mix_n.outputs[0], mat_n.inputs[0])
tree.links.new(mat_n.outputs[0], output_n.inputs[0])
tree.links.new(mat_n.outputs[1], output_n.inputs[1])
return mat
def htmlColorToIntRGBArray(str):
if(len(str) != 7 or not str.startswith('#')):
raise Exception("Parsing of color config file failed.")
rgba = [None] * 4
rgba[0] = int(str[1:3], 16) / 255
rgba[1] = int(str[3:5], 16) / 255
rgba[2] = int(str[5:7], 16) / 255
rgba[3] = 1
return rgba
def load(filepath, ldconfig, library, global_scale, use_remove_doubles):
if(len(colorsDict) == 0):
ColorSheet(ldconfig)
LegoParser(filepath, library, use_remove_doubles, global_scale)
return {'FINISHED'}

File Metadata

Mime Type
text/x-c++
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
3e/c1/1a5f659de1b325afeee90d08c501