#!BPY
"""
Name: 'Scat and Drop Objects'
Blender: 248
Group: 'Object'
Tooltip: ' Scatter and drop objects'
"""
# ***** BEGIN GPL LICENSE BLOCK ***** 
# 
# This program is free software; you can redistribute it and/or 
# modify it under the terms of the GNU General Public License 
# as published by the Free Software Foundation; either version 2 
# of the License, or (at your option) any later version. 
# 
# This program is distributed in the hope that it will be useful, 
# but WITHOUT ANY WARRANTY; without even the implied warranty of 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
# GNU General Public License for more details. 
# 
# You should have received a copy of the GNU General Public License 
# along with this program; if not, write to the Free Software Foundation, 
# Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. 
# 
# ***** END GPL LICENCE BLOCK ***** 
# -------------------------------------------------------------------------- 

####################################################################
# Scatter , Drop and zTrack        Jimmy Hazevoet  ,  Feb./May 2005
#-------------------------------------------------------------------
#_ GENERATE COPIES:
#  1: Select all the objects you want to scatter
#  2: Hit the 'Select' and 'Assign' buttons
#  3: Set number of Copies, set Location, Rotation and Size options
#  4: Hit the 'Scatter' button
#
#__   RANDOMISE SELECTED OBJECTS:
#     1: Select all the objects you want to Randomise (scatter)
#     2: set Location, Rotation and Size options
#     3: Hit the 'Scatter' button
#
#___     DROP OBJECTS TO GROUND MESH:
#        1: Be sure that the 'Ground' is below the objects yuo want to Drop
#        2: Fill in the Ground mesh Name
#        3: Select all the objects you want to Drop
#        4: Hit 'Drop to Ground' button 
#        (with many objects and a dense ground mesh this can take some time)
#
#        NOTE:Be sure that Ground normals are pointing upwards,
#        and Apply Size/Rotation [ Ctrl-A ] of your Ground mesh.
#        'Drop to Ground' uses the Object Center for the objects to be dropped,
#        so place them appropriately.
#
#____    zTrack:
#        Orients selected objects (probably cards)
#        toward the object named "Camera"
#        but only rotates their Z axis, so the cards remain upright.
#        Great for trees and forests.
#
#####################################################################

#####################################################################
# Drop Objects to Ground   ## script on lines 240,340 ##
# by Harkyman aka Roland Hess
## license: Do whatever you want with it.
#
# This Script will drop all objects in the
# current selection onto the mesh object
# named 'Ground'.
# The script uses the mesh of Ground, so you
# can use irregular shapes like mountainsides,
# stairsteps, etc.It uses the Object Center
# for the objects to be dropped, so place them
# appropriately.
# So make your ground, select your objects to drop,
# and hit Alt-P! ( Drop to Ground Button )
#################################################################


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

curscene = Scene.GetCurrent()
nobjs = 0
objects = []

###------------------------------------------------------------------
##
#--------------

GName = Create('Ground')   #-#- Drop to Ground: Ground name
copy_rand = Create(1)      #-#- Toggle: generate copies / randomise objects
nuobs = Create(10)         #-#- Number of copies to generate

obLoXmin = Create(-10.0)   #-#- Minimal Object Location X
obLoYmin = Create(-10.0)   #-#- Minimal Object Location Y
obLoZmin = Create(0.0)     #-#- Minimal Object Location Z
obLoXmax = Create(10.0)    #-#- Maximal Object Location X
obLoYmax = Create(10.0)    #-#- Maximal Object Location Y
obLoZmax = Create(0.0)     #-#- Maximal Object Location Z

obRoXmin = Create(0.0)     #-#- Minimal Object Rotation X
obRoYmin = Create(0.0)     #-#- Minimal Object Rotation Y
obRoZmin = Create(0.0)     #-#- Minimal Object Rotation Z
obRoXmax = Create(0.0)     #-#- Maximal Object Rotation X
obRoYmax = Create(0.0)     #-#- Maximal Object Rotation Y
obRoZmax = Create(180.0)   #-#- Maximal Object Rotation Z

obSiXmin = Create(0.0)     #-#- Minimal Object Size X
obSiYmin = Create(0.0)     #-#- Minimal Object Size Y
obSiZmin = Create(0.0)     #-#- Minimal Object Size Z
obSiXmax = Create(0.0)     #-#- Maximal Object Size X
obSiYmax = Create(0.0)     #-#- Maximal Object Size Y
obSiZmax = Create(0.0)     #-#- Maximal Object Size Z

Smin = Create(1.0)         #-#- Minimal Object Uniform Size
Smax = Create(1.0)         #-#- Maximal Object Uniform Size

uni = Create(1)            #-#- Toggle Size Uniform: on/off
siz = Create(1)            #-#- Toggle Size x,y,z: on/off
rot = Create(1)            #-#- Toglle Rotation: on/off
loc = Create(1)            #-#- Toggle Location: on/off


###------------------------------------------------------------------
## reset
#-------------
def reset():
	global nuobs
	global obLoXmin, obLoYmin, obLoZmin, obLoXmax, obLoYmax, obLoZmax
	global obRoXmin, obRoYmin, obRoZmin,obRoXmax, obRoYmax, obRoZmax
	global obSiXmin, obSiYmin, obSiZmin,obSiXmax, obSiYmax, obSiZmax, Smin, Smax
	global uni, siz, rot, loc

	uni = Create(1)
	siz = Create(1)
	rot = Create(1)
	loc = Create(1)
	nuobs  = Create(10)
	obLoXmin = Create(0.0)
	obLoYmin = Create(0.0)
	obLoZmin = Create(0.0)
	obLoXmax = Create(0.0)
	obLoYmax = Create(0.0)
	obLoZmax = Create(0.0)
	obRoXmin = Create(0.0)
	obRoYmin = Create(0.0)
	obRoZmin = Create(0.0)
	obRoXmax = Create(0.0)
	obRoYmax = Create(0.0)
	obRoZmax = Create(0.0)
	obSiXmin = Create(0.0)
	obSiYmin = Create(0.0)
	obSiZmin = Create(0.0)
	obSiXmax = Create(0.0)
	obSiYmax = Create(0.0)
	obSiZmax = Create(0.0)
	Smin = Create(1.0)
	Smax = Create(1.0)

#--------------------------------
## Create random numbers --------------------------
###---------------------------
def randnum(low,high):
   num = Blender.Noise.random()
   num = num*(high-low)
   num = num+low
   return num

# --------------------------------------------------
## Make a Copy of An Object --- Like Alt D --- #
###-------------------------------------------------
def MakeCopy(obj):
   scn = Scene.getCurrent()
   type = obj.getType()
   newobj = Object.New(type)
   newobj.shareFrom(obj)
   newobj.setSize(obj.getSize())
   newobj.setEuler(obj.getEuler())
   scn.link(newobj)
   newobj.drawMode = obj.drawMode
   newobj.Layer = obj.Layer
   obj.select(0)
   scn.update(1) 
   return newobj

#--------------------------------------------------------------------
## Randomise Object Location, Rotation, Size 
###------------------------------------------------------------------
def obrand(ob):
	global obLoXmin, obLoYmin, obLoZmin, obLoXmax, obLoYmax, obLoZmax
	global obRoXmin, obRoYmin, obRoZmin,obRoXmax, obRoYmax, obRoZmax
	global Smin, Smax, obSiXmin, obSiYmin, obSiZmin,obSiXmax, obSiYmax, obSiZmax
	global uni, siz, rot, loc

	# Size uniform ---------------------------------
	if uni.val !=0:
		rnds = randnum(Smin.val, Smax.val)
		ob.SizeX = ob.SizeX * rnds
		ob.SizeY = ob.SizeY * rnds
		ob.SizeZ = ob.SizeZ * rnds

	# Size x,y,z -----------------------------------
	if siz.val !=0:
		ob.SizeX += randnum(obSiXmin.val , obSiXmax.val)
		ob.SizeY += randnum(obSiYmin.val , obSiYmax.val)
		ob.SizeZ += randnum(obSiZmin.val , obSiZmax.val)

	# Rotation -------------------------------------
	if rot.val !=0:
		ob.RotX = ob.RotX + 2 * randnum(obRoXmin.val / 180.0 * pi , obRoXmax.val / 180.0 * pi)
		ob.RotY = ob.RotY + 2 * randnum(obRoYmin.val / 180.0 * pi , obRoYmax.val / 180.0 * pi)
		ob.RotZ = ob.RotZ + 2 * randnum(obRoZmin.val / 180.0 * pi , obRoZmax.val / 180.0 * pi)

	# Location -------------------------------------
	if loc.val !=0:
		x = randnum(obLoXmin.val , obLoXmax.val)
		y = randnum(obLoYmin.val , obLoYmax.val)
		z = randnum(obLoZmin.val , obLoZmax.val)
		ob.setLocation(x,y,z)


#--------------------------------------------------------------------
## Scatter Copies
###------------------------------------------------------------------
def scatter():
	global nuobs
	###-----------------------
	for i in range(nuobs.val):
		# Random Object Copy ---------------------------
		ro = int( randnum( 0, len( objects ) ) )
		ob = MakeCopy( objects[ro] )
		ob.select(1)
		# Randomise ---------------------------
		obrand(ob)
	return ob

#--------------------------------------------------------------------
## Randomise Selected Objects
###------------------------------------------------------------------
def randomise():
	randobjects = Blender.Object.GetSelected()
	# -----------------------
	for ob in randobjects:
		# Randomise ---------------------------
		obrand(ob)

############################################################
#--------------------------------------------------------------------
## zTrack, by Harkyman aka Roland Hess.
###------------------------------------------------------------------
def zTrack():
   obloop=Object.GetSelected()
   camera = Object.Get("Camera")
   cameraloc=camera.getLocation()
   for plane in obloop:
      planeloc = plane.getLocation()
      dx=cameraloc[0] - planeloc[0]
      dy=cameraloc[1] - planeloc[1]
      planeZ= atan(dx/dy)
      planerot = plane.getEuler()
      print planerot[0],planerot[1],planeZ	
      plane.setEuler(planerot[0],planerot[1],-planeZ)


############################################################
#-----------------------------------------------------------
## Drop2Ground.py , by Harkyman aka Roland Hess.
###---------------------------------------------------------
# Drop Objects to Ground
# by Harkyman aka Roland Hess
##license: Do whatever you want with it.
###---------------------------------------------------------

def sideofline(PA,PB,PP):
	return ((PA[0]-PP[0])*(PB[1]-PP[1]))-((PB[0]-PP[0])*(PA[1]-PP[1]))

def InsideQuad(p0,p1,p2,p3,p4):
	if (sideofline(p1,p3,p0)>=0):
		return (sideofline(p1,p4,p0)<=0) and (sideofline(p4,p3,p0)<=0)
	else:
		return (sideofline(p1,p2,p0)>=0) and (sideofline(p2,p3,p0)>=0)

def InsideTri(p0,p1,p2,p3):
	return (sideofline(p1,p2,p0)>=0) and (sideofline(p2,p3,p0)>=0) and (sideofline(p3,p1,p0)>=0)

def FindZ(TriangleFace,tx,ty,tz,px,py):
	[A,B,C]=TriangleFace.no
	D=-((A*tx)+(B*ty)+(C*tz))
	return -((A*px)+(B*py)+D)/C

def InverseTranslate(InCoords,DeltaVec):
	return [InCoords[0]+DeltaVec[0],InCoords[1]+DeltaVec[1],InCoords[2]+DeltaVec[2]]

###--------------------------------------
def Drop():
	global GName

	print '===Drop to Ground - Start==='

	Ground = Blender.Object.Get (GName.val)
	InvVec = Ground.getLocation ()

	#determine min and max bounding values of the ground object
	#so we can ignore selected objects not even over the ground
	GroundBounds = Ground.getBoundBox ()
	GBminX = 0
	GBminY = 0
	GBmaxX = 0
	GBmaxY = 0

	for [gbx, gby, gbz] in GroundBounds:
		#adjust for Ground object location
		gbx = gbx + InvVec[0]
		gby = gby + InvVec[1]
		gbz = gbz + InvVec[2]

		if GBminX > gbx:
			GBminX = gbx
		else:
			if GBmaxX < gbx:
				GBmaxX = gbx
		if GBminY > gby:
			GBminY = gby
		else:
			if GBmaxY < gby:
				GBmaxY = gby

	#get the list of selected objects to drop
	FloatingObjects = Blender.Object.GetSelected ()

	#get the face list of the ground object
	GroundMesh = Blender.NMesh.GetRawFromObject (GName.val)

	for DropObject in FloatingObjects:
		[DropX, DropY, DropZ] = DropObject.getLocation ()
		#is the object above the ground's bounding box?
		if ((DropX <= GBmaxX) and (DropX >= GBminX) and (DropY >=GBminY) and (DropY <= GBmaxY)):
			#find which face the object is over - if the
			#object isn't over a ground face, do nothing
			GroundFaces = GroundMesh.faces
			for GroundFace in GroundFaces:
				GroundVerts = GroundFace.v
				if len(GroundVerts) == 4:
					#check for quad faces
					[x1,y1,z1] = InverseTranslate(GroundVerts[0].co,InvVec)
					[x2,y2,z2] = InverseTranslate(GroundVerts[1].co,InvVec)
					[x3,y3,z3] = InverseTranslate(GroundVerts[2].co,InvVec)
					[x4,y4,z4] = InverseTranslate(GroundVerts[3].co,InvVec)
					p0=[DropX,DropY,0]	
					p1=[x1,y1,0]
					p2=[x2,y2,0]
					p3=[x3,y3,0]
					p4=[x4,y4,0]
					if InsideQuad(p0,p1,p2,p3,p4):
						#print 'Moved ', DropObject, ' to a Quad face.'
						DropObject.setLocation(DropX,DropY,FindZ(GroundFace,x1,y1,z1,DropX,DropY))				
				else:
					#check for tri faces
					[x1,y1,z1] = GroundVerts[0].co
					[x2,y2,z2] = GroundVerts[1].co
					[x3,y3,z3] = GroundVerts[2].co
					p0=[DropX,DropY,0]	
					p1=[x1,y1,0]
					p2=[x2,y2,0]
					p3=[x3,y3,0]
					if InsideTri(p0,p1,p2,p3):
						#print 'Moved ', DropObject, ' to a Tri face.'
						DropObject.setLocation(DropX,DropY,FindZ(GroundFace,x1,y1,z1,DropX,DropY))
		
	print '===Drop to Ground - Done==='
#------------------------------------------------------------------
## Drop2Ground.py , by Harkyman aka Roland Hess.
###----------------------------------------------------------------
###################################################################


#-------------------------------------------------------------------
## general user interface
###-----------------------------------------------------------------
def gui():
	global nobjs, GName
	global nuobs, copy_rand
	global obLoXmin, obLoYmin, obLoZmin, obLoXmax, obLoYmax, obLoZmax
	global obRoXmin, obRoYmin, obRoZmin,obRoXmax, obRoYmax, obRoZmax
	global Smin, Smax
	global obSiXmin, obSiYmin, obSiZmin,obSiXmax, obSiYmax, obSiZmax
	global uni, siz, rot, loc

	glClearColor(0.66,0.66,0.66, 1)
	glClear(GL_COLOR_BUFFER_BIT)
	glColor3f(0.61,0.61,0.61)
	glRecti(5, 5, 265, 245)
	glColor3f(0.73,0.73,0.73)
	glRecti(5, 55, 265, 225)
	glColor3f(1.0, 1.0, 1.0)
	glRasterPos2f(15, 230)
	Text("Scatter.",'small')
	glColor3f(0.0, 0.0, 0.0)
	glRasterPos2f(15, 213)
	Text("Objects",'small')

	if copy_rand.val == 1:
		Button("Select", 5, 60, 190, 50, 20, "Append selected objects to scatter")
		if nobjs !=0:
			glColor3f(0.0, 0.6, 0.0)
		else:
			glColor3f(0.9, 0.0, 0.0)
		glRasterPos2f(130, 213)
		Text(str(nobjs))
		Button("Assign", 6, 110, 190, 50, 20, "Assign selected objects to scatter")
		glColor3f(0.0, 0.0, 0.0)
		glRasterPos2f(165, 213)
		Text("Copies",'small')
		nuobs = Number(":",2, 160,190,50,20, nuobs.val, 1,100, "Number of copies")
		crpos=160
		crwid=50
		crtxt= "Copies"
		crtip= "Generate Copies. (Toggle: Generate Copies / Randomise Selected)"
	else:
		crpos=10
		crwid=200
		crtxt= "Randomise Selected Objects."
		crtip= "Randomise Selected Objects. (Toggle: Generate Copies / Randomise Selected)"
	copy_rand = Toggle(crtxt,2, 10,190,crwid,20, copy_rand.val, crtip)

	Button("Scatter", 3, 210,190,50,20,"Scatter")

	glRasterPos2f(30, 173)
	Text("Location",'small')
	loc = Toggle("L",2, 10,170,15,15, loc.val, "Location: on/off")
	if loc.val !=0:
		obLoXmin = Number("X",2,10,155,60,15, obLoXmin.val,-1000.0, 1000.0,"Loc.X Min.")
		obLoYmin = Number("Y",2,10,140,60,15, obLoYmin.val,-1000.0, 1000.0,"Loc.Y Min.")
		obLoZmin = Number("Z",2,10,125,60,15, obLoZmin.val,-1000.0, 1000.0,"Loc.Z Min.")
		obLoXmax = Number("X",2,70,155,60,15, obLoXmax.val,-1000.0, 1000.0,"Loc.X Max.")
		obLoYmax = Number("Y",2,70,140,60,15, obLoYmax.val,-1000.0, 1000.0,"Loc.Y Max.")
		obLoZmax = Number("Z",2,70,125,60,15, obLoZmax.val,-1000.0, 1000.0,"Loc.Z Max.")

	glRasterPos2f(160, 173)
	Text("Rotation",'small')
	rot = Toggle("R",2, 140,170,15,15, rot.val, "Rotation: on/off")
	if rot.val !=0:
		obRoXmin = Number("X",2,140,155,60,15, obRoXmin.val,-360.0, 360.0, "Rot.X Min.")
		obRoYmin = Number("Y",2,140,140,60,15, obRoYmin.val,-360.0, 360.0, "Rot.Y Min.")
		obRoZmin = Number("Z",2,140,125,60,15, obRoZmin.val,-360.0, 360.0, "Rot.Z Min.")
		obRoXmax = Number("X",2,200,155,60,15, obRoXmax.val,-360.0, 360.0, "Rot.X Max.")
		obRoYmax = Number("Y",2,200,140,60,15, obRoYmax.val,-360.0, 360.0, "Rot.Y Max.")
		obRoZmax = Number("Z",2,200,125,60,15, obRoZmax.val,-360.0, 360.0, "Rot.Z Max.")

	glRasterPos2f(30, 108)
	Text("Size x,y,z",'small')
	siz = Toggle("S",2, 10,105,15,15, siz.val, "Size x,y,z: on/off")
	if siz.val !=0:
		obSiXmin = Number("X",2,10,90,60,15, obSiXmin.val,0.0, 100.0, "Size X Min.")
		obSiYmin = Number("Y",2,10,75,60,15, obSiYmin.val,0.0, 100.0, "Size Y Min.")
		obSiZmin = Number("Z",2,10,60,60,15, obSiZmin.val,0.0, 100.0, "Size Z Min.")
		obSiXmax = Number("X",2,70,90,60,15, obSiXmax.val,0.0, 100.0, "Size X Max.")
		obSiYmax = Number("Y",2,70,75,60,15, obSiYmax.val,0.0, 100.0, "Size Y Max.")
		obSiZmax = Number("Z",2,70,60,60,15, obSiZmax.val,0.0, 100.0, "Size Z Max.")

	glRasterPos2f(160, 108)
	Text("Size Uniform",'small')
	uni = Toggle("U",2, 140,105,15,15, uni.val, "Size Uniform: on/off")
	if uni.val !=0:
		Smin = Number("Size Min",2,140,90,120,15, Smin.val,0.0, 100.0, "Minimal Size")
		Smax = Number("Size Max",2,140,75,120,15, Smax.val,0.0, 100.0, "Maximal Size")

	glColor3f(1.0, 1.0, 1.0)
	glRasterPos2f(150, 45)
	Text("zTrack.",'small')
	ztip="Orients selected objects (cards) toward the object named Camera but only rotates their Z axis. Great for trees and forests.(zTrack made by: Harkyman)"
	Button("zTrack", 7, 140,25,120,15,ztip)

	glColor3f(1.0, 1.0, 1.0)
	glRasterPos2f(15, 45)
	Text("Drop to Ground.",'small')
	GName = String("OB:",2,10,25,120,15,GName.val,128,"Ground Object(Mesh) Name.")
	Button("Drop to Ground", 4, 10,10,120,15,"Drop selected objects to Ground mesh (Drop to Ground made by: Harkyman)")

	Button("X", 1, 245,227,15,15,"Exit / Reset")


###------------------------------------------------------------------
def event(evt, val):
	if (evt== QKEY and not val):
		Exit()
###----------------------------------
def bevent(evt):
	global nobjs, objects, copy_rand

	if evt == 1:
		name = "OK ?%t|Exit %x1|Reset %x2"
		result = Blender.Draw.PupMenu(name)
		if result == 2:
			reset()
			Blender.Redraw()
		if result == 1:
			Exit()

	if (evt == 2):
		Draw()

	if (evt == 3):
		Blender.Window.WaitCursor(1)
		if copy_rand.val == 0:
			randomise()
		else:
			if nobjs !=0:
				scatter()
		Blender.Window.WaitCursor(0)
		Blender.Redraw()

	if (evt == 4):
		try:
			Blender.Window.WaitCursor(1)
			Drop()
			Blender.Window.WaitCursor(0)
		except:
			return 0		
		Blender.Redraw()
		
	if (evt == 7):
		Blender.Window.WaitCursor(1)
		zTrack()
		Blender.Window.WaitCursor(0)		
		Blender.Redraw()
	
	# --------------------------------------------------
    # --- Select objects to scatter --------------------
	if evt == 6:
		nobjs = len(Blender.Object.GetSelected())
		if nobjs > 0:
			objects = Blender.Object.GetSelected()
		Register(gui, event, bevent)
	if evt == 5:
		nobjs = len(objects) + len(Blender.Object.GetSelected())
		if nobjs > 0:
			for obj in Blender.Object.GetSelected():
				objects.append(obj)
		Register(gui, event, bevent)

Register(gui, event, bevent)