Object spray script

hi below is a new version MUCH cleaner and faster (to see the dif just change artistic to TRUE and multiply it by the time you hold the mouse down)

the interactive part will come later this week (hope) with a draw spacehandler

and the code is GPL even is i still need to add a license

# SPACEHANDLER.VIEW3D.EVENT

import Blender, bpy, BPyWindow, BPyMesh
from Blender import *
from Blender.Window import *
from Blender.Mathutils import *
from math import sin, cos, sqrt
import math

"""
Name: 'Mesh Spray'
Blender: 248
Group: 'Spacehandeler'
Tip: 'add an object called "Sphere" select another object paint with RMB'
"""

# HOW TO USE:
# 1 - load the script in Blender's text editor
# 2 - enable it as spacehandelr in the 3dview menu
# 3 - add an object caled "Sphere" this can be any object just rename if necesary (hardcoded when i make the gui you will be abel to use any Linked object in the scene)
# 4 - use RMB to spray the "Sphere" over the selected object

"""
#for now this script only draws objects caled "Sphere" and 
#links them to the layer of the object we are drawing on 

"""
#if you want to change the num of copys change Max to the num of copys 
#to change the "brush" size change radius 
#to make the object to use the orientation of the face change normal to "TRUE"

#personaly i use the select with "Left Mouse" option if you use "Right Mouse" you may want to change "mouse_buttons == 4:" to "mouse_buttons == 1:"

#i still nead to coment the code and add a GUI 
#any feedback welcomed at [email protected] or LinkCanabico at the #blendercoders forum 

radius=100
CenterX=0
CenterY=0
Max=50
RAND_MAX=1

randX=[]
randY=[]
copy=[]
normal=True
sel=4
artistic=FALSE
evt = Blender.event
visibles=0
XYvisible = []

def meshIntersects(me, Origin, Direction):
    def faceIntersects(f):
        isect = Intersect(f.v[0].co, f.v[1].co, f.v[2].co, Direction, Origin, 1) # Clipped.
        if isect:
            return isect
        elif len(f.v) == 4:
            isect = Intersect(f.v[0].co, f.v[2].co, f.v[3].co, Direction, Origin, 1) # Clipped.
        return isect

    isect= best_isect= None
    dist_from_orig= 1<<30
    
    for f in me.faces:
        isect= faceIntersects(f)
        if isect:
            return TRUE
    return FALSE

def factorial(n): return reduce(lambda x,y:x+y, xrange(1,n+1))

def randInCircle (num, radi):
    X=[]
    Y=[]
    for x in xrange(0, num):
        Angle = Rand(0,1)*6.283185307
        Mag = Rand(0,1)*radi
        Mag = radi-(Mag * Mag)/radi
        X.append(int((cos(Angle) * Mag)))
        Y.append(((sin(Angle) * Mag)))
    return X, Y

def Circle (num, radi):
    X=[]
    Y=[]
    Angle=0
    for x in range(0, num):    
        Angle = x * math.pi * 2.0 / num
        X.append(int(radi * cos(Angle)))
        Y.append(int(radi * sin(Angle)))
    return X, Y

def lineCircle (num, radi, ite=5):
    X=[]
    Y=[]
    Angle=0
    tnum=int(num/ite)
    tradi=radi/ite
    radi=tradi
    for i in range(0, ite):
        for x in range(0, tnum):    
            Angle = x * math.pi * 2.0 / tnum
            X.append(int(radi * cos(Angle)))
            Y.append(int(radi * sin(Angle)))
        radi=radi+tradi


    return X, Y

def Spiral (num, radi, inv=FALSE):
    X=[]
    Y=[]
    step=radi/num
    radi=0    
    for x in xrange( 0,num):
        if not inv:
            X.append(int(radi*cos(x)))
            Y.append(int(radi*sin(x)))
        else:
            X.append(int(radi*sin(x)))
            Y.append(int(radi*cos(x)))
        radi=radi-step
    return X, Y

def closedShel (num, radi, inv=FALSE):
    X=[]
    Y=[]
    step=radi/num    
    radi=0
    for x in xrange( 0,num):
        Angle = x * math.pi * 2.0 / num
        X.append(int(radi * cos(Angle)))
        Y.append(int(radi * sin(Angle)))
        if not inv:
            radi=radi+step
        else:
            radi=radi+step
    return X, Y

def openShel (num, radi, inv=FALSE):
    X=[]
    Y=[]
    step=radi/num    
    radi=0
    for x in xrange( 0,num):
        Angle = radi * math.pi * 2.0 / num
        X.append(int(radi * cos(Angle)))
        Y.append(int(radi * sin(Angle)))
        if not inv:
            radi=radi+step
        else:
            radi=radi-step
    return X, Y

mouse_buttons = Window.GetMouseButtons()
if mouse_buttons == 4:

    scn = Scene.GetCurrent()
    terrain = scn.objects.active
    m=terrain.getData()
    
    if sel==0:    
        randX, randY = randInCircle(Max,radius)
    elif sel==1:    
        randX, randY = Circle(Max,radius)
    elif sel==2:    
        randX, randY = Spiral(Max,radius)
    elif sel==3:    
        randX, randY = closedShel(Max,radius)
    elif sel==4:    
        randX, randY = openShel(Max,radius)
    elif sel==5:    
        randX, randY = lineCircle(Max,radius,5)

    while mouse_buttons==4:
        mouse_buttons=Window.GetMouseButtons()
        mc=GetMouseCoords()
    for i in range(0, Max):
        ray    =    BPyWindow.mouseViewRay(    randX[i]+mc[0],randY[i]+mc[1],terrain.matrix)
        ori,dir= ray[1],ray[2]
        if ray[0] and ray[1]:
            if meshIntersects(m,ori,dir):
                XYvisible.append([])
                XYvisible[visibles].append(randX[i]+mc[0])
                XYvisible[visibles].append(randY[i]+mc[1])
                copy.append(Object.New('Mesh'))
                visibles=visibles+1
    for i in range(0, visibles):
        copy[i].link(bpy.data.objects["Sphere"].getData(mesh=1))
        scn.objects.link(copy[i])
        ray    =    BPyWindow.mouseViewRay(    XYvisible[i][0],XYvisible[i][1],terrain.matrix)
        ori,dir= ray[1],ray[2]
        f=BPyMesh.pickMeshRayFace(m,ori,dir)
    
        if f[0]:
            M0=terrain.matrix
            vs=f[0].v
            v0=Vector(vs[0])*M0
            v1=Vector(vs[1])*M0
            v2=Vector(vs[2])*M0
            eX, e=(v1-v0).normalize(), v2-v0
            eZ=CrossVecs(eX,e).normalize()
            eY=CrossVecs(eX,eZ)
            M=Matrix(-eX,eY,eZ)
            if normal:
                copy[i].setMatrix(M)
            copy[i].setLocation(f[1]*M0)
            copy[i].sel=0
            terrain.sel=1
            print "ADDED ELEMENT: ", i
        if artistic:
            Redraw()
    Redraw()
    Blender.event=None

No more crashes here :slight_smile:

Some suggestions:

  1. add the objects as a Dupli-Copy (ie Alt+D)

  2. randomly change the size of the objects

Minor update now you can set minimal and maximal scale randomnes for each axis
and a maximal random rotation for each

Also you can now create a “Brush” mesh (name is important, will change as soon i make the GUI:() where each vertex represents the relative position of the final copy (from top view) please NOTE the radius will scale the end result to adapt it to the “standard” size

Last but most important (IMHO) each brush stroke adds all objects it draws to a group

dfelinto: it now uses Dupli-copy

# SPACEHANDLER.VIEW3D.EVENT

import Blender, bpy, BPyWindow, BPyMesh
from Blender import *
from Blender.Window import *
from Blender.Mathutils import *
from math import sin, cos, sqrt
import math

"""
Name: 'Mesh Spray'
Blender: 248
Group: 'Spacehandeler'
Tip: 'add an object called "Sphere" select another object paint with RMB'
"""

# HOW TO USE:
# 1 - load the script in Blender's text editor
# 2 - enable it as spacehandelr in the 3dview menu
# 3 - add an object caled "Sphere" this can be any object just rename if necesary (hardcoded when i make the gui you will be abel to use any Linked object in the scene)
# 4 - use RMB to spray the "Sphere" over the selected object

"""
#for now this script only draws objects caled "Sphere" and 
#links them to the layer of the object we are drawing on 

"""
#if you want to change the num of copys change Max to the num of copys 
#to change the "brush" size change radius 
#to make the object to use the orientation of the face change normal to "TRUE"

#personaly i use the select with "Left Mouse" option if you use "Right Mouse" you may want to change "mouse_buttons == 4:" to "mouse_buttons == 1:"

#i still nead to coment the code and add a GUI 
#any feedback welcomed at [email protected] or LinkCanabico at the #blendercoders forum 

radius=100
CenterX=0
CenterY=0
Max=50
RAND_MAX=1

randX=[]
randY=[]
copy=[]
normal=True
sel=1

artistic=FALSE
evt = Blender.event
visibles=0
XYvisible = []

minXscale =.5
maxXscale =1.5
minYscale =.5
maxYscale =1.5
minZscale =.5
maxZscale =1.5

randXrot=0
randYrot=0
randZrot=360

def meshIntersects(me, Origin, Direction):
    def faceIntersects(f):
        isect = Intersect(f.v[0].co, f.v[1].co, f.v[2].co, Direction, Origin, 1) # Clipped.
        if isect:
            return isect
        elif len(f.v) == 4:
            isect = Intersect(f.v[0].co, f.v[2].co, f.v[3].co, Direction, Origin, 1) # Clipped.
        return isect

    isect= best_isect= None
    dist_from_orig= 1<<30
    
    for f in me.faces:
        isect= faceIntersects(f)
        if isect:
            return TRUE
    return FALSE

def factorial(n): return reduce(lambda x,y:x+y, xrange(1,n+1))

def randInCircle (num, radi):
    X=[]
    Y=[]
    for x in xrange(0, num):
        Angle = Rand(0,1)*6.283185307
        Mag = Rand(0,1)*radi
        Mag = radi-(Mag * Mag)/radi
        X.append(int((cos(Angle) * Mag)))
        Y.append(((sin(Angle) * Mag)))
    return X, Y

def Circle (num, radi):
    X=[]
    Y=[]
    Angle=0
    for x in range(0, num):    
        Angle = x * math.pi * 2.0 / num
        X.append(int(radi * cos(Angle)))
        Y.append(int(radi * sin(Angle)))
    return X, Y

def lineCircle (num, radi, ite=5):
    X=[]
    Y=[]
    Angle=0
    tnum=int(num/ite)
    tradi=radi/ite
    radi=tradi
    for i in range(0, ite):
        for x in range(0, tnum):    
            Angle = x * math.pi * 2.0 / tnum
            X.append(int(radi * cos(Angle)))
            Y.append(int(radi * sin(Angle)))
        radi=radi+tradi
    return X, Y

def Spiral (num, radi, inv=FALSE):
    X=[]
    Y=[]
    step=radi/num
    radi=0    
    for x in xrange( 0,num):
        if not inv:
            X.append(int(radi*cos(x)))
            Y.append(int(radi*sin(x)))
        else:
            X.append(int(radi*sin(x)))
            Y.append(int(radi*cos(x)))
        radi=radi-step
    return X, Y

def closedShel (num, radi, inv=FALSE):
    X=[]
    Y=[]
    step=radi/num    
    radi=0
    for x in xrange( 0,num):
        Angle = x * math.pi * 2.0 / num
        X.append(int(radi * cos(Angle)))
        Y.append(int(radi * sin(Angle)))
        if not inv:
            radi=radi+step
        else:
            radi=radi+step
    return X, Y

def openShel (num, radi, inv=FALSE):
    X=[]
    Y=[]
    step=radi/num    
    radi=0
    for x in xrange( 0,num):
        Angle = radi * math.pi * 2.0 / num
        X.append(int(radi * cos(Angle)))
        Y.append(int(radi * sin(Angle)))
        if not inv:
            radi=radi+step
        else:
            radi=radi-step
    return X, Y

def fromtopmesh(brush):
    polo=brush.boundingBox
    mesh=brush.getData(mesh=1)
    x=polo[0][0]
    y=polo[0][1]
    brush_r=0
    scaler=0
    for i in range(0,8):
        if (polo[i][0] > x) or (polo[i][0]*-1) > x:
            if polo[i][0] >0:
                x=polo[i][0]
            else:
                x=(polo[i][0]*-1)
        if (polo[i][1] > y) or (polo[i][1]*-1) > y:
            if polo[i][1] >0:
                y=polo[i][1]
            else:
                y=(polo[i][1]*-1)
    if x> y:
        brush_r=x
    else:
        brush_r=y
    scaler= radius/brush_r
    
    X=[]
    Y=[]
    for i in mesh.verts:
        X.append(i.co[0]*scaler)
        Y.append(i.co[1]*scaler)
    return X, Y
        
mouse_buttons = Window.GetMouseButtons()
if mouse_buttons == 4:
    scn = Scene.GetCurrent()
    terrain = scn.objects.active
    m=terrain.getData()

    grp= Group.New()
    
    if sel==0:    
        randX, randY = randInCircle(Max,radius)
    elif sel==1:    
        randX, randY = Circle(Max,radius)
    elif sel==2:    
        randX, randY = Spiral(Max,radius)
    elif sel==3:    
        randX, randY = closedShel(Max,radius)
    elif sel==4:    
        randX, randY = openShel(Max,radius)
    elif sel==5:    
        randX, randY = lineCircle(Max,radius,5)
    elif sel==6:
        randX, randY = fromtopmesh(Blender.Object.Get ('Brush'))

    while mouse_buttons==4:
        mouse_buttons=Window.GetMouseButtons()
        mc=GetMouseCoords()
    for i in range(0, len(randX)):
        ray    =    BPyWindow.mouseViewRay(    randX[i]+mc[0],randY[i]+mc[1],terrain.matrix)
        ori,dir= ray[1],ray[2]
        if ray[0] and ray[1]:
            if meshIntersects(m,ori,dir):
                XYvisible.append([])
                XYvisible[visibles].append(randX[i]+mc[0])
                XYvisible[visibles].append(randY[i]+mc[1])
                #copy.append(Object.New('Mesh'))
                visibles=visibles+1
    terrain.sel=0
    for i in range(0, visibles):
        orig = Blender.Object.Get ('Sphere')
        orig.sel=1
        Blender.Object.Duplicate()
        copy= scn.objects.active
        
        #copy[i].link(bpy.data.objects["Sphere"].getData(mesh=1))
        #scn.objects.link(copy[i])

        grp.objects.link(copy)
        ray    =    BPyWindow.mouseViewRay(    XYvisible[i][0],XYvisible[i][1],terrain.matrix)
        ori,dir= ray[1],ray[2]
        f=BPyMesh.pickMeshRayFace(m,ori,dir)
    
        if f[0]:
            M0=terrain.matrix
            vs=f[0].v
            v0=Vector(vs[0])*M0
            v1=Vector(vs[1])*M0
            v2=Vector(vs[2])*M0
            eX, e=(v1-v0).normalize(), v2-v0
            eZ=CrossVecs(eX,e).normalize()
            eY=CrossVecs(eX,eZ)
            M=Matrix(-eX,eY,eZ)
            if normal:
                copy.setMatrix(M)
            copy.setLocation(f[1]*M0)
             #Random Scale
            scale = Rand(minXscale,maxXscale)
            copy.SizeX = scale
            scale = Rand(minYscale,maxYscale)
            copy.SizeY = scale
            scale = Rand(minZscale,maxZscale)
            copy.SizeZ = scale
            #Random Rotation
            copy.RotX = 2 * math.pi * Rand(0,randXrot)
            copy.RotY = 2 * math.pi * Rand(0,randYrot)
            copy.RotZ = 2 * math.pi * Rand(0,randZrot)

            copy.sel=0
        if artistic:
            Redraw()
    terrain.sel=1
    Redraw()
    Blender.event=None