Laminate script

I am sure that there’s an easier way to do this - but I have created a script that creates a copy of a mesh such that the faces of the mesh are indented a small amount from the origional. eg. if applied to a sphere it creates a smaller concentric sphere. I want it to also work with much more complex shapes.

it works by walking through the list of faces and creating new vertices, offset a small distance from each origional vertex in a direction normal to the origional face.

If I create faces from these vertices, the new faces overlap at the edges making a very jagged shape, so I consolodate all vertices within a culling radius into a single value, forcing the faces to meet at the edges (well it’s supposed to) In practice it only works for very small offsets that can usefully use a small culling radius otherwise innocent bystanding points get culled too and the shape is whittled awat to a nub.

The vertex culling and consolidation algorithm stinks so I’m open to any better suggestions.

What I really would like is for someone to take the time to run the script (if anyone can follow a word of this explanation) and suggest ways in which I might improve the algorithms (improvement in my coding style is probably a dead loss)

The defaults in the gui are suitable to create a reasonabl
e shrunken monkey head from the one in the list of standard meshes in Blender Publisher.

you’ll need to cut the monkey’s head open or move the origional aside to see the very slighly smaller copy.

Many thanks to
csDraco_ and S68 via neil.

Here’s the script…

from Blender import *
from math import *
from Blender.Draw import *


false = 0
true = 1
offset = Create(0.01)
cullradius = Create(0.01)
indent = offset.val
newfaces=[]
faces=[]
facerefs = []
points = []
me = Mesh.New()
ob = Object.New('Mesh', 'nested')
sc = Scene.getCurrent()

def Warn():
	BGL.glRasterPos2d(115, 23) 
	Blender.Window.Redraw(Blender.Window.Const.TEXT)
    
def draw():
	global offset, cullradius 
	BGL.glClearColor(0.5, 0.7, 0.5, 1) 
	BGL.glColor3f(1,1,1) 
	BGL.glClear(BGL.GL_COLOR_BUFFER_BIT)
	BGL.glColor3f(1, 1, 0) 
	BGL.glRasterPos2d(10, 170)
	sc = Scene.getCurrent()
	objects = Object.GetSelected() 
	if len(objects) > 0 : Text("Set the indent value and press Go - small indents work best")
	else: Text("Select a mesh object to work with")
	BGL.glRasterPos2d(10, 150)
	Text("Blender Nested object maker - (L) Dec. 2002 Daddy") 
	offset=Slider("Offset ",2,10, 100, 250, 20, offset.val, 0, 0.1)
	cullradius=Slider("Cull radius ",2,10, 120, 250, 20, cullradius.val, 0.001, 1.0) 
	Button("Break", 1 ,40 , 70, 40, 20)
	Button("Go", 3, 120, 70, 40, 20)
  

    
def event(evt, val):
	if (evt== QKEY and not val):
		Exit()

def bevent(evt):
	global faces, data, indent, mindistance
	if   (evt== 1):
		Exit()
	elif (evt== 2):
		Draw()
	elif (evt== 3):
		objects = Object.GetSelected()
		print len(objects)
		if len(objects) < 1: Draw()
		else:
			object = objects[0]
			data = object.data
			faces = data.faces
			indent = offset.val
			mindistance = cullradius.val
			make_shell()
			Exit()

def crossprod (u, v):
	return (((u[1]*v[2])-(u[2]*v[1])),-1.0*((u[0]*v[2])-(u[2]*v[0])), ((u[0]*v[1])-(u[1]*v[0])))

def dotprod (u,v):
	return (u[0]*v[0])+(u[1]*v[1])+(u[2]*v[2])

def distance (p1, p2):
	return sqrt(((p1[0]-p2[0])**2)+((p1[1]-p2[1])**2)+((p1[2]-p2[2])**2))

def normalise(vector):
	factor = sqrt((vector[0]**2)+(vector[1]**2)+(vector[2]**2))/indent
	if factor <= 0.000001:
		print "warning - zero normal"
		return (0.0, 0.0, 0.0)
	else:
		return (vector[0]/factor, vector[1]/factor, vector[2]/factor)

def normal (plane):
	vector_1 = (plane[1].co[0] - plane[0].co[0], plane[1].co[1] - plane[0].co[1], plane[1].co[2] - plane[0].co[2])
	vector_2 = (plane[2].co[0] - plane[0].co[0], plane[2].co[1] - plane[0].co[1], plane[2].co[2] - plane[0].co[2])
	return normalise(crossprod(vector_1, vector_2))

def consolidate (point):
	for counter in range (0, len(points)):
		if distance(point,points[counter][0]) <= mindistance:
			points[counter].append(point)
			return counter
	points.append([point])
	return (len(points)-1)



def make_shell():

	global offset, indent, points, objects, object, data, faces, newfaces, facerefs, ob, sc, me


	for face in faces:
		mugs=[]
		if len(face) == 3:
			mugs=[[face[0], face[1], face[2]]]
		elif len(face) == 4:
			mugs=[[face[0],face[1],face[3]]]
			mugs.append([face[1],face[2],face[3]])
		for mug in mugs:
			newface=[]
			norm = normal(mug)
			for vertex in mug: 
				newface.append(consolidate((vertex.co[0]-norm[0],vertex.co[1] - norm[1],vertex.co[2]-norm[2])))
			facerefs.append(newface)   

	for i in range (0,len(points)):
		newx = 0.0
		newy = 0.0
		newz = 0.0

		n = len(points[i])
		m = 0.0+n
		for j in range (0,n):
			newx += points[i][j][0]
			newy += points[i][j][1]	
			newz += points[i][j][2]
		points[i]=(me.addVert((newx/m, newy/m, newz/m)))	

	for face in facerefs:
		newface=[]
		for vertex in face: 
			newface.append(points[vertex])
		newfaces.append(newface)
		me.addFace(newface)

	points = []
	facerefs=[]
	ob.link(me)
	sc.link(ob)

Register (draw, event, bevent)

I think you make it more complicated than it needs to be, you could just use the vertex normal to offset the vertices directly. Would be faster as well. You can of course also just use alts-s in editmode in blender to do something similar, unless you maybe want to create several scaled copies at once.
I have done something like this once, on my site you can find a script to create shells around a mesh scaled using the vertex normal (fake fur bunny). It doesn’t work in publisher though, but you could easily change that.

many thanks eeshlo. I had never used alt-s in edit mode before. it seems to do exactly what I want. I still want a script though., so I can automate this and so I can specify the “laminate” thickness.

I was puzzling over how a vertex could have a normal. I think I will define the vertex normal as the sum of the normals to the faces that share that vertex.

do I have to calculate the face normals myself or is there somewhere I can pull them out of the mesh object ?

Also, is there an easy way to get a list of all faces that share a vertex ?

I see the normal information in your fur script - thanks again eeshlo

Ohhh,

very nice script.

BTW, when I want do do something like that I usually use DupliFrames… can you compare the two techniques?

Stefano