Feb 182011
Blender 2.56 Python Script for an animation system 2D gravity/accretion simulation. I was intended to be sort of an artistic tool but the physics are correct. I still have some UI work to do but it’s on the back burner for right now. Anyway it works if you feel like playing with it.
Features:
-2d gravity/accretion of ‘glob’ objects
-adjustable (G)ravitational constant (not in UI)
1 blender unit = a(AU)
1 mass unit = m(Solar Mass)
24frames = t(sidereal year)
G = sqrt((2*pi/t)*(sqrt(a**2/m)))
Instructions:
-open Blender2.56b
-copy or load the script into the text editor and run script
-ui will be in physics panel
-adjust parameters
-create globs and/or sun/holes and/or add/adjust vector tails
-animate globs
################################################## # # Simple Orbital Mechanics - v.0.0.01a - 01.30.11 # GPL, Public Licence, Open-Source, Free, etc... # 2D Gravity/Accretion Simuatior # # Thanks to Thanassis Tsiodras for his #"Naive simulator of gravity, in less than 200 Python lines" # http://users.softlab.ntua.gr/~ttsiod/gravity.html # it got me on the right track # # >Physics Property Menu >Simple Orbital Mechanics # -adjust parameters # >createGobs, can create multiple times # and move if desired # >createSuns, or name other objects with prefix 'sun' # and move if desired # ############################################################## # # TODO/WISHLIST: # - other objects # arbitrary objects # - impliment negative mass? repulsive matter? # other implimentations of gravity? # pushes at a distance, # - variable gravity? # ! - make 2dplane arbitrary somehow # maybe give each object a xyz reconfig # so you can reassing so you can have # various systems interacting on diff # plane 1st would just # be to reassign axis on keyframe # -make sure Z loc is not locked in any way # so i can play with stacking them # vertically, setup a sim, then # start camera top and dolly side to reveal dist... # - materials (PlanetTex patch?) # - sim analysis # run thru varions configs # keep those with best 'stability' # to make visually interesting sims # ie. most mass stays inside bounds # or most non merged mass in bounds # and most movement in bounds? # - other movement formulas? # maybe impliment fractal flames somehow though # that's probably another project # - drive other params such as colors with loc/vel/acc # - option for dynamic rotation? # - color picker # vectors or other settings as color picker wheel? # lock to a black and use as vector? # -smaller regular partical emitter at ever merge # -also, maybe make all objects, attractors?# # # KNOWN ISSUES # - accumulation of data - .unlink, # need to figure something else out # !!! have flush but crashes blender... # - cannot rotate vectortails directly in # 3d view, only in 'n' panel. scale works though... # - CONSTRAINTS effecting start frame position # running sim should not change start frame # THE frame 1 JUMP IS ACTUALLY BECAUSE OF CONSTRANT/SOMETHING # or updating inital velocity # - clear animation data -FIX- # - sun' still getting location keyframed # - metballs don't really appear... #################################################### ###???ACRETION MERGERGLOB OFFSET ISN'T RIGHT. STILL # ALLOWS OFFSET WAY BEYOND RADIUS ###!!!3daxis interchange - just put a cust value for XYZ -> ZYX mappting # then adjust just before set location ###???clear animation should also clear paths? ###!!!add gloobject count to panel ###???not making very good use of the index idea... # probably get to that when I refactor for arbitary xyz # global scn, GI # GIsuns, GIglobs = [], [] # GI = [GIsuns, GIglobs] # GI(index) will help with all kinds of stuff.. # also work in more build in fxns like vector ang - xy converions for tails and whatnot ###!!!also make time of partlife relative to framerun ###!!!ADD PROPULSION???? somehow add thrust/nav controls? # to try out different nav techniques ###???metaballs, explode into other metaballs and add into GI # maybe newly spawned objects are only effected by parent? # new custom value = generation... (1st, 2nd, 3rd, etc) # OR split metaELEMTS! # eventuallty make so metaball impacts create # new objects and those are then calced in gravity... ###!!!also - next progream... # dedicated script to gravitize a selection of any # objects based on bounding box... # ... try to make this one reletivistic # incding lorenz... try to help visualize # reletivity on pc # time dilation??? # reletivistic mass? all velocities based on % of maximum (essentially c) # then incorporate E=mc2 (somehow so harder to accelerate the faster # it's going... # faster to use GI index on animate # especuially if use keep 'merged' in index... ###!!!LATER... metaball element trails... ###???option to explode at clip? <-------------- ###!!!on create mesh path verify there is anim data ###!!!lockxy rotation on tails ###???figure out subdivision... # context thing in panel? ###!!! add event to starting velocity # make vecto suns toggle withevent... so you can change bac to random ###!!! maybe instead of merging with constraints, just # seup so each loop if its merged it sets keyframe to its parents # this could make axis-remapping easier in cases of merging # #################################################### ### NEXT NEXT NEXT NEXT NEXT NEXT NEXT NEXT NEXT #################################################### ###!!!----------->figure out dist to sun relative to earth size... ###!!!----------->radial glob field create option for kieper/oort 35-55au ###!!!Voyager...speed 3.5 au/yr - exit trajectory from sol #then ui# #then refactor for release 00000.2 whaever #no core refactor, just loose ends, cleanup... #then post!!! #then sleep? #need some kind of collision damping ############################################################## #-maybe some issues with significant digits at some point # i think python floats are 28 digits? #-something about mass from radius... """sun = 1M = 1.98892e30 kg mercury 3.3010e23/1.98892e30 venus 4.1380e24/1.98892e30 mars 6.4273e23/1.98892e30 jupiter 1.89852e27/1.98892e30 saturn 5.6846e26/1.98892e30 uranus 8.6819e25/1.98892e30 neptune 1.02431e26/1.98892e30""" #figure out what a frame = in this setup ### got a lot of stuff working # but... accretion offset is still wonky... # and... tmass fcurve is CURVED # need to set curves to CONSTANT for merged, tmass, etc... ###!!! its adding constrainy to emptys? ######################### DEFAULTS/CONFIG/SETTINGS ######################### import bpy import sys import random import time from math import pi, sqrt, sin, cos, asin, acos """ #derived from one mercury orbit-per-sec 1.2252211349000193/framerate = AU/frame 1 simsecond = 1 mercuryyear = 0.240 earthyear 365.25*24*60*60 = 31557600 sec/yr 0.240*31557600 = 7573824.07573824.0 1 simsecond = 7573824.07573824 realseconds --->1 simframe = 0.240*31557600/framerate realseconds 365*24*60*60 = 31536000 sec/yr (earth) c = 63188.25au/yr c = 63188.25/31536000 au/sec c = 0.0020036862633181127 au/sec G = 6.67300e-11 m**3/kg**-1/s**2 F=G(m1*m2/r**2) """ ### I THINK I HAVE SCALE/SIZE RIGHT ###NOW NEED SOMETHING FOR KG AND SECONDS #figure out G for this rigwith m/s/kg from mybases scn = bpy.context.scene #-dimensions FRAMERUN = 300 #ui length of simulation #AU = 1.0 #ui 1BlenderUnit = 1AU WIDTH = 150 #ui Blender units (should I add a scale option?) HEIGHT = 150 CLIP = 1000 #ui stop/ignore objects too far out STARTFRAME = 1 #ui start frame of simulation #-globs/sunholes PARTICLES = 5 #ui number globs to add #FRAMERATE = scn.RenderSettings.fps FRAMERATE = 24 TIMESCALE = 1.0 #based on framerate, au, and cno idea... 1y? #--->1 simframe = 0.240*31557600/framerate realseconds #EdMASS = 1.0 #ui??? Earth = 1unit: to be used for mass scaling and such #SOLMASS = 5.9736e24 A = 1.0 #astronomical unit = 149.60e6 km = 149.60e9 m M = 1.0 #solar mass = 1.98892e30 kg T = 365.256363 #sidereal year #D = 1.0 #mean solar day = 86400.0025 SI seconds @ epoch J1900 #gaussian constant = 0.01720209895 A**2/3 D*-1 M*-1/2 G = sqrt((2*pi/T)*(sqrt(A**2/M))) #GM = GRAVITY * S #(GM product) #GRAVITY = G #ui effect of gravity #SOLDIAM = 1.0 ###!!!should be sol diam in AU? #SOLDIAM = 8.517493549379003e-05 #Earth's diameter constant # 1AU = 149598000 km # EarthDiameter = 12742.0 km # EarthDiameter = 12742/149598000 AU # DSIZE = 250 # Solar unit draw size of glob mesh # no effect on physics AXMAP = ['X','Y','Z']#default axismapping SIZEVAR = 1.0 #ui variation in glob size in feild generation #SsUNRADIUS = 109.0 #ui default sun radius (same as Sol) SUNRANDLOC = True #ui default setting for random sun loc #SUNsDSIZE = 1.0 #ui draw size of suns #-physics DAMP = 1.0 #ui temp use for vel and acc (1.0 = no damp) MAXVEL = 20.0 #ui max velocity MAXINTERAD = CLIP #ui max dist between 2 particles too calc force DAMPVEL = 1.0 #na damp or add velocity (1.0 = 100%) DAMPACC = 1.0 #na damp or add acceleration (less makes more spinny animations) DAMPVELx = DAMPVEL #na in case someone wants asymetrical damping later DAMPVELy = DAMPVEL DAMPACCx = DAMPACC DAMPACCy = DAMPACC RANDVEL = True #ui generate random starting velocity RANDVELRANGE = .125 #ui random range for staring velocity VECMULT = 10000 #ui multiplier for vector tails should be just display LOOPATCLIP = False #ui option to loop at clip dist ACCRETE = True #ui EXPLODE = True #ui EXTHRESH = .25 #ui total impact velocity required to explode METABALLS = False #-internal GPREFIX = 'glob' #fn prefix for globjects MOBNAME = 'mglob' #fn name of globject meshdata SPREFIX = 'sun' #fn name of sun objects ###!!! replace the prefix system with a 'glob' variable print('----------GEAUX!!!----------') ################### playground ###################### #-dist, size, mass, based on Earth = 1 def createSolSystem(): ###!!!OortCloud 2000-50,000 au ###!!!ProximaCentauri 268000 au ###!!!add Europa, Phobos, Demos, lots o' moons to add ###!!!saturns rings? NOZERO = 0.00000000001 print('make a mass-distance-speed / scale accurate copy of sol') solG2V = makeMeshCube('sun-SOLG2V', scn.DSIZE) solG2V.name = 'sun-SOLG2V' solG2V.location = [0.0, 0.0, 0.0] solG2V.scale = [scn.SOLDIAM*109, scn.SOLDIAM*109, scn.SOLDIAM*109] solG2V.tmass = scn.M solG2V.modifiers.new('sunbevel', 'BEVEL') solG2V.show_name = True scn.objects.link(solG2V) mercury = createMetaBall('glob-MERCURY', scn.DSIZE) mercury.name = 'glob-MERCURY' mercury.location = [-0.39, NOZERO, 0.0] mercury.scale = [scn.SOLDIAM*0.3829, scn.SOLDIAM*0.3829, scn.SOLDIAM*0.3829] mercury.tmass = scn.M*0.055 mercury.vel = [0.0, -0.24, 0.0] mercury.show_name = True mercury.merged = False scn.objects.link(mercury) venus = createMetaBall('glob-VENUS', scn.DSIZE) venus.name = 'glob-VENUS' venus.location = [NOZERO, 0.72, 0.0] venus.scale = [scn.SOLDIAM*0.949, scn.SOLDIAM*0.949, scn.SOLDIAM*0.949] venus.tmass = scn.M*0.82 venus.vel = [-0.62, 0.0, 0.0] venus.show_name = True venus.merged = False scn.objects.link(venus) earth = createMetaBall('glob-EARTH', scn.DSIZE) earth.name = 'glob-EARTH' earth.location = [1.0, NOZERO, 0.0] earth.scale = [scn.SOLDIAM, scn.SOLDIAM, scn.SOLDIAM] earth.tmass = scn.M earth.vel = [0.0, 1.0, 0.0]###??? EARTH BASED VELOCITY? SURE WHY NOT earth.show_name = True earth.merged =False scn.objects.link(earth) ###??? wrong though, it's orbital period, not velocity... ### luna = createMetaBall('glob-LUNA', scn.DSIZE) luna.name = 'glob-LUNA' luna.location = [1.026, NOZERO, 0.0] luna.scale = [scn.SOLDIAM*0.273, scn.SOLDIAM*0.273, scn.SOLDIAM*0.273] luna.tmass = scn.M*0.012 luna.vel = [0.0, 1.0, 0.0] luna.show_name = True luna.merged = False scn.objects.link(luna) mars = createMetaBall('glob-MARS', scn.DSIZE) mars.name = 'glob-MARS' mars.location = [NOZERO, -1.52, 0.0] mars.scale = [scn.SOLDIAM*0.532, scn.SOLDIAM*0.532, scn.SOLDIAM*0.532] mars.tmass = scn.M*0.11 mars.vel = [1.88, 0.0, 0.0] mars.show_name = True mars.merged = False scn.objects.link(mars) jupiter = createMetaBall('glob-JUPITER', scn.DSIZE) jupiter.name = 'glob-JUPITER' jupiter.location = [-5.204267, NOZERO, 0.0] jupiter.scale = [scn.SOLDIAM*11.209, scn.SOLDIAM*11.209, scn.SOLDIAM*11.209] jupiter.tmass = scn.M*317.8 jupiter.vel = [0.0, -11.86, 0.0] jupiter.show_name = True jupiter.merged = False scn.objects.link(jupiter) saturn = createMetaBall('glob-SATURN', scn.DSIZE) saturn.name = 'glob-SATURN' saturn.location = [NOZERO, 9.582, 0.0] saturn.scale = [scn.SOLDIAM*9.449, scn.SOLDIAM*9.449, scn.SOLDIAM*9.449] saturn.tmass = scn.M*95.2 saturn.vel = [-29.46, 0.0, 0.0] saturn.show_name = True saturn.merged = False scn.objects.link(saturn) uranus = createMetaBall('glob-URANUS', scn.DSIZE) uranus.name = 'glob-URANUS' uranus.location = [19.22, NOZERO, 0.0] uranus.scale = [scn.SOLDIAM*4.007, scn.SOLDIAM*4.007, scn.SOLDIAM*4.007] uranus.tmass = scn.M*14.6 uranus.vel = [0.0, 84.01, 0.0] uranus.show_name = True uranus.merged = False scn.objects.link(uranus) neptune = createMetaBall('glob-NEPTUNE', scn.DSIZE) neptune.name = 'glob-NEPTUNE' neptune.location = [NOZERO, -30.06, 0.0] neptune.scale = [scn.SOLDIAM*3.883, scn.SOLDIAM*3.883, scn.SOLDIAM*3.883] neptune.tmass = scn.M*17.2 neptune.vel = [164.8, 0.0, 0.0] neptune.show_name = True neptune.merged = False scn.objects.link(neptune) pluto = createMetaBall('glob-PLUTO', scn.DSIZE) pluto.name = 'glob-PLUTO' pluto.location = [-29.74, NOZERO, 0.0] pluto.scale = [scn.SOLDIAM*0.19, scn.SOLDIAM*0.19, scn.SOLDIAM*0.19] pluto.tmass = scn.M*0.002 pluto.vel = [0.0, -248.09, 0.0] pluto.merged =False pluto.show_name = True scn.objects.link(pluto) sedna = createMetaBall('glob-SEDNA', scn.DSIZE) sedna.name = 'glob-SEDNA' sedna.location = [NOZERO, 960.0, 0.0] sedna.scale = [scn.SOLDIAM*0.2197, scn.SOLDIAM*0.2197, scn.SOLDIAM*0.2197] sedna.tmass = scn.M*0.0004185081023168609 sedna.vel = [-118090.0, 0.0, 0.0] sedna.show_name = True sedna.merged = False scn.objects.link(sedna) voyager = makeMeshCube('glob-VOYAGER', scn.DSIZE) voyager.name = 'glob-VOYAGER1' voyager.location = [-115.0, NOZERO, 0.0] voyager.scale = [scn.SOLDIAM*1.5696123057604772e-06, scn.SOLDIAM*1.5696123057604772e-06, scn.SOLDIAM*1.5696123057604772e-06] voyager.tmass = scn.M*1.2086513994910943e-22 voyager.vel = [0.0, -10.0, 0.0] scn.objects.link(voyager) voyager.show_name = True voyager.merged = False def makeGlobField(): for i in range(scn.PARTICLES): gx = random.uniform(-scn.WIDTH/2, scn.WIDTH/2) gy = random.uniform(-scn.HEIGHT/2, scn.HEIGHT/2) gz = 0.0 #2D sim for now #gs = random.uniform(scn.SOLDIAM-(scn.SOLDIAM*(scn.SIZEVAR/2)), # scn.SOLDIAM+(scn.SOLDIAM*(scn.SIZEVAR/2))) #sun diameter = 1391000 kilometers #au = 149598000 kilometers #sun diameter = 1391000/149598000 AU #sun diameter = 0.009298252650436503 AU gs = 0.009298252650436503 #-make variable? this makes default sun size our sun if METABALLS: newob = createMetaBall('metaglobule', scn.DSIZE) globname = GPREFIX + 'meta-' + str(i+1000)[1:4] else: newob = makeMeshCube(MOBNAME, scn.DSIZE) globname = GPREFIX + '-' + str(i+1000)[1:4] newob.name = globname newob.location = [gx, gy, gz] newob.scale = [gs, gs, gs] #add custom properties newob.merged = False newob.bhole = False #newob.tmass = scn.M *random.uniform(1, SIZEVAR) newob.tmass = scn.M # should be a random mass var newob.vel = [0.0, 0.0, 0.0] #newob.density = scn.STDENSITY if scn.RANDVEL: gvx = random.uniform(-RANDVELRANGE/2, RANDVELRANGE/2) gvy = random.uniform(-RANDVELRANGE/2, RANDVELRANGE/2) gvz = 0.0 newob.vel = [gvx, gvy, gvz] scn.objects.link(newob) def createMetaBall(mname, msize): msize = msize/2 #-radius from diameter #-have to go into edit mode to refesh metaobj for some reason mball = bpy.data.metaballs.new(mname) for m in range(1): mel = mball.elements.new() #mel.type = 'BALL' mel.co = [0.0, 0.0, 0.0] mel.size_x = msize mel.size_y = msize mel.size_z = msize #mel.stiffness = 1.0 #mel.use_negative = False #could be fun oball = bpy.data.objects.new('metaglobule', mball) oball.data = mball oball.glob = True return(oball) def makeMeshCube(mname, msize): msize = msize/2 #-radius from diameter mmesh = bpy.data.meshes.new(mname) mmesh.vertices.add(8) mmesh.vertices[0].co = [-msize, -msize, -msize] mmesh.vertices[1].co = [-msize, msize, -msize] mmesh.vertices[2].co = [ msize, msize, -msize] mmesh.vertices[3].co = [ msize, -msize, -msize] mmesh.vertices[4].co = [-msize, -msize, msize] mmesh.vertices[5].co = [-msize, msize, msize] mmesh.vertices[6].co = [ msize, msize, msize] mmesh.vertices[7].co = [ msize, -msize, msize] mmesh.faces.add(6) mmesh.faces[0].vertices_raw = [0,1,2,3] mmesh.faces[1].vertices_raw = [0,4,5,1] mmesh.faces[2].vertices_raw = [2,1,5,6] mmesh.faces[3].vertices_raw = [3,2,6,7] mmesh.faces[4].vertices_raw = [0,3,7,4] mmesh.faces[5].vertices_raw = [5,4,7,6] mmesh.update(calc_edges=True) omesh = bpy.data.objects.new(mname, mmesh) omesh.data = mmesh omesh.glob = True return(omesh) def makeSunHole(): holemass = 10 ll = [] for m in scn.objects: if m.name[0:len(SPREFIX)] == SPREFIX: ll.append(m.name) gx = random.uniform(-scn.WIDTH/2, scn.WIDTH/2) gy = random.uniform(-scn.HEIGHT/2, scn.HEIGHT/2) gz = 0 gs = 0.009298252650436503 #-sol diam in AU newsun = makeMeshCube('sunmesh', scn.DSIZE) newsun.name = SPREFIX + '-' +str(len(ll)+1000)[1:4] if scn.SUNRANDLOC: newsun.location = [gx, gy, gz] newsun.scale = [gs, gs, gs] newsun.modifiers.new('sunbevel', 'BEVEL') newsun.merged = False newsun.bhole = True newsun.tmass = scn.M * holemass scn.objects.link(newsun) def clearAnimation(): for o in scn.objects: for f in range(scn.FRAMERUN): scn.frame_current = f+1 if o.name[0:len(GPREFIX)] == GPREFIX or o.name[0:len(SPREFIX)] == SPREFIX: o.keyframe_delete('location') o.keyframe_delete('vel') o.keyframe_delete('tmass') o.keyframe_delete('merged') for c in o.constraints: c.keyframe_delete('influence') o.constraints.remove(c) # QQQ remove constraints influence frames too? def createMeshPath(): ###!!! set this up to use ob.glob but not ob.bhole #-this is UGLY, need to find how to get fcurve data easier for ob in scn.objects: if ob.name[0:len(GPREFIX)] == GPREFIX: # #and len(GPREFIX)+4 == len(ob.name): #-hope avoid making path for path locX, locY, locZ, frame = [], [], [], [] for f in range(scn.STARTFRAME, scn.FRAMERUN+1): oanim = ob.animation_data ocurves = oanim.action.fcurves addframe = 0 stopvert = 5 #-some hacky bullshit to make merged blobs paths right for ocurve in ocurves: #-only add to array if object hasn's been merged. !!! BROKE! if ocurve.data_path == 'merged' and ocurve.evaluate(f) == False: addframe = 1 stopvert = 0 for ocurve in ocurves: if addframe == 1: if ocurve.data_path == 'location' and ocurve.array_index == 0: locX.append(ocurve.evaluate(f)) if ocurve.data_path == 'location' and ocurve.array_index == 1: locY.append(ocurve.evaluate(f)) #-dont create motion paths under a % of FRAMERUM PATHTHRESH = 20 #%<-----make a ui variable (or setup file) if len(locX)/scn.FRAMERUN < PATHTHRESH/scn.FRAMERUN: print(len(locX)/scn.FRAMERUN, PATHTHRESH/scn.FRAMERUN) continue mpath = bpy.data.meshes.new('meshpath') mpath.use_auto_smooth = True for p in range(len(locX)-1): if p == 0 or p == len(locX)-1: mpath.vertices.add(1) mpath.vertices[len(mpath.vertices)-1].co = [locX[p], locY[p], 0.0] if p > 0 and p < len(locX)-1-stopvert: x2, y2 = locX[p-1], locY[p-1] x1, y1 = locX[p+1], locY[p+1] xM, yM = locX[p], locY[p] dix, diy = x2 - x1, y2 - y1 hyp = sqrt(dix**2 + diy**2) if hyp == 0: hyp = 0.000000000000000000001 ang = acos(dix/hyp) perpL = ang + pi/4 perpR = ang - pi/4 ###???maybe rotate the angle dynamically? #-multiplying 1/hyp here makes width # inv proportional to velocity # because dist bt verts grow with vel, so does hyp ###???WORKS SORT OF, it's SOMETHING WITH THAT /0 hyp thing? #velT = (1/ hyp)*2 #<====-----make a ui variable? velT = .5 nx1 = cos(perpL)*velT ny1 = sin(perpL)*velT nx2 = cos(perpR)*velT ny2 = sin(perpR)*velT nxL = xM + nx1 nyL = yM + ny1 nxR = xM + nx2 nyR = yM + ny2 ZTIMESCALE = 50 #percent of framrun ztime = p*(ZTIMESCALE/100) #<====-----make a ui variable # and/or proportonal to wid/height mpath.vertices.add(1) mpath.vertices[len(mpath.vertices)-1].co = [ nxL, nyL, ztime] #-centerline, same as loc... #mpath.vertices.add(1) #mpath.vertices[len(mpath.vertices)-1].co = [ # xM, yM, ztime] mpath.vertices.add(1) mpath.vertices[len(mpath.vertices)-1].co = [ nxR, nyR, ztime] mpath.update() #-DO NOT face last 4 verts or b will crash (last -1 one is for index for fc in range(1, len(mpath.vertices)-5, 4): mpath.faces.add(1) mpath.faces[len(mpath.faces)-1].vertices_raw = [ fc, fc+2, fc+3, fc+1] mpath.faces.add(1) mpath.faces[len(mpath.faces)-1].vertices_raw = [ fc+3, fc+2, fc+4, fc+5] mpath.update(calc_edges=True) mpath.update(calc_edges=True) opath = bpy.data.objects.new('path'+ob.name, mpath) scn.objects.link(opath) ######################################################## def addExplosivo(obQ, vx, vy): if len(obQ.data.faces) < 512: #-have to subdivide cube first scn.objects.active = obQ cuts = len(obQ.data.faces) // 512 ###???don't lnow how to do this with ops like this #bpy.ops.mesh.subdivide(number_cuts = cuts, fractal = 0.25) ###??? maybe use fluids? or randomize settings? obQ.modifiers.new(name = 'explosemit', type = 'PARTICLE_SYSTEM') expart = obQ.particle_systems[0] #expart.settings.count = 512 # 8x8x8 cube grid expart.settings.count = 6 # for now expart.settings.rotation_factor_random = 2.5 expart.settings.frame_start = scn.frame_current expart.settings.frame_end = scn.frame_current + 1 expart.settings.lifetime = 120 expart.settings.lifetime_random = 1 expart.settings.use_dynamic_rotation = True expart.settings.render_type = 'NONE' expart.settings.draw_method = 'NONE' expart.settings.rotation_factor_random = 1.0 expart.settings.factor_random = 10.0 expart.settings.object_align_factor = [vx*vx*vx, vy*vy*vy, 0] #expart.settings.object_factor = 2 expart.settings.effector_weights.gravity = 0.0 expart.settings.brownian_factor = .25 #expart.settings.particle_size = 1 #expart.settings.use_multiply_size_mass = True #expart.settings.use_size_deflect = True #expart.settings.size_random = 2.5 ###??? child particles? exmod = obQ.modifiers.new(name = 'explosivo', type = 'EXPLODE') exmod.show_dead = False exmod.use_edge_split = True #!!!bpy.ops.ptcache.bake(bake=True) now # or bpy.ops.ptcache.bake_all() at the end of loop? #-set all globs vectors tangent to the nearest sun # hopefully will create more interesting sims def setVecTangentToNearestHole(): #-trying set tan to nearest ob if no sun... holeindex = [] for hole in scn.objects: if hole.bhole: holeindex.append(hole) tempmult=35 if len(holeindex)<1: for gobs in scn.objects: if gobs.glob: holeindex.append(gobs) tempmult=10 print(holeindex) for gob in scn.objects: if gob.glob and gob.bhole == False: x1, y1 = gob.location[0], gob.location[1] nholeinit = True for hole in holeindex: if hole.name != gob.name: #if sol != gob: x2, y2 = hole.location[0], hole.location[1] dix, diy = x2 - x1, y2 - y1 disq = dix*dix + diy*diy dri = sqrt(disq) # find nearest Sun if nholeinit: nholeinit = False nhole = hole nholedri = dri nholex = dix nholey = diy continue if dri < nholedri: nhole = hole nholedri = dri nholex = dix nholey = diy vang, vmag = getVectorAngle(nholex, nholey, 0.0, False) #-random if vector is clockwz/anticlock to sun and if # magnitude is proportional/invesely to dist to sun coinflip = random.uniform(0,1) coinflip = 1 #cheat for testing if coinflip >=.333: vangNEW = vang + (pi/2) vmagNEW = (1/nholedri)*tempmult #-should include mass of second object in calc... if coinflip < .333: vangNEW = vang - (pi/2) vmagNEW = vmag * nholedri / VECMULT for c in gob.children: if c.name[0:7] == 'vectail': c.rotation_euler = [0.0, 0.0, vangNEW] c.scale = [vmagNEW, vmagNEW, 0] hyp = vmagNEW / scn.VECMULT cvx = cos(vangNEW)*hyp cvy = sin(vangNEW)*hyp gob.vel = [cvx, cvy, 0.0] def updateVecFromTail(globV): kids = globV.children for c in kids: # get vx and vy back from rot, scale vecang = c.rotation_euler[2] vecmag = c.scale[0] hyp = vecmag / scn.VECMULT cvx = cos(vecang)*hyp cvy = sin(vecang)*hyp globV.vel = [cvx, cvy, 0.0] def getVectorAngle(vx, vy, vz, hyp): if hyp == False: hyp = (sqrt((vx*vx)+(vy*vy))) # avoid div-0, hacky I know... if hyp == 0: hyp = .0000000000001 if vx >= 0 and vy >= 0: # -I vecang = asin(vy/hyp) if vx <= 0 and vy >= 0: # -II vecang = pi-asin(vy/hyp) if vx <= 0 and vy <= 0: # -III vecang = -pi-asin(vy/hyp) if vx >= 0 and vy <= 0: # -IV vecang = asin(vy/hyp) vecmag = (hyp*scn.VECMULT) #this makes return value only good for tail DISPLAY return(vecang, vecmag) def flush(): killall = bpy.data.objects for deadmesh in killall: if deadmesh.glob or deadmesh.name[0:7] == "vectail": # #or deadmesh.name[0:len(SPREFIX)] == SPREFIX: if deadmesh.users == 0: bpy.data.objects.remove(deadmesh) else: print(deadmesh.name + ' is being stubborn...') scn.objects.link(deadmesh) for deadob in killall: if deadob.glob or deadob.name[0:7] == "vectail": # # or deadob.name[0:len(SPREFIX)] == SPREFIX: if deadob.users == 0: bpy.data.objects.remove(deadob) else: print(deadob.name + ' is being stubborn...') scn.objects.link(deadob) ######################### PHYSICS FXNS ######################### ######################### GLOB/SUN FXNS ######################### def updateAllVecFromTail(): for obs in scn.objects: if obs.glob: updateVecFromTail(obs) def updateTailFromVec(globV): kids = globV.children if globV.merged == False and len(kids) > 0: vecang, vecmag = getVectorAngle( globV.vel[0], globV.vel[1], globV.vel[2], False) for c in kids: c.rotation_euler = [0.0, 0.0, vecang] c.scale = [vecmag, vecmag, 0.0] c.keyframe_insert('rotation_euler', 2) c.keyframe_insert('scale', 0) c.keyframe_insert('scale', 1) def makeVectorTail(globV): mmesh = bpy.data.meshes.new('vectail') mmesh.vertices.add(2) mmesh.vertices[0].co = [0.0, 0.0, 0.0] mmesh.vertices[1].co = [6.0, 0.0, 0.0] mmesh.edges.add(1) mmesh.edges[0].vertices = [0,1] mmesh.update() omesh = bpy.data.objects.new('vectail', mmesh) scn.objects.link(omesh) omesh.parent = globV omesh.data = mmesh omesh.hide_render = True vecang, vecmag = getVectorAngle( globV.vel[0], globV.vel[1], globV.vel[2], False) omesh.rotation_euler = [0.0, 0.0, vecang] omesh.scale = [vecmag, vecmag, 0] def addVectorTails(): for obs in scn.objects: if obs.glob: if len(obs.children) > 0: for kid in obs.children: try: scn.objects.unlink(kid) except: print('i dont get unlinking...') makeVectorTail(obs) def removeObject(xob): xmesh = xob.data scn.objects.unlink(xob) try: bpy.data.objects.remove(xmesh) except: print('---cannot remove ' + xmesh.name + '---') try: bpy.data.objects.remove(ob) except: print('---cannot remove ' + xob.name + '---') def delSuns(): for xall in scn.objects: if xall.bhole: removeObject(xall) #bpy.context.scene.objects.unlink(xall) selectCheck() def delGlobs(): for xall in scn.objects: if xall.glob or xall.name[0:7] == 'vectail' or xall.name[0:8] == 'pathglob': removeObject(xall) selectCheck() def selectCheck(): #-make sure there's an object active so panel doesn't disappear if scn.objects.active: return else: if len(scn.objects) > 1: scn.objects.active = scn.objects[0] else: newempty = bpy.ops.object.add() newempty.hide = False newempty.hide_select = False newempty.hide_render = False scn.objects.link(newempty) scn.objects.active=newempty """ def makeGlobIndex(): GI = [] obs = scn.objects for ob in obs: if ob.glob: #or ob.name[0:len(SPREFIX)] == SPREFIX: GI.append(ob.name) return GI """ ######################### RUNLOOPS ######################### #def runit(GI): def runit(): for globA in scn.objects: if globA.glob: #print(globA.name, ' is a glob') #for i in range(0, len(GI)): #globA = obs[GI[i]] #globA = glob if globA.bhole: continue #-don't calc force on blackholes (immovable) if globA.merged: continue #-don't calc force on merged Ax, Ay = globA.location[0], globA.location[1] ### ??? ### add option for round cliploop? # check for CLIP (border) if sqrt(Ax*Ax+Ay*Ay) > scn.CLIP: if scn.LOOPATCLIP: #-if loopatclip set, loop glob around if abs(Ax) > scn.CLIP: globA.location[0] = -Ax if abs(Ay) > scn.CLIP: globA.location[1] = -Ay else: #-otherwise stop and mark as merged so it'll be ignored globA.merged = True continue #-skip to next globA loop if globA outside CLIP # temp loc, vel and acc vars for loop tloc = globA.location tvel = globA.vel tacc = [0.0, 0.0, 0.0] #for j in range(0, len(GI)): for globB in scn.objects: if globB.glob: if globB == globA or globB.merged: continue #globB = obs[GI[j]] #globB = glob2 Bx, By = globB.location[0], globB.location[1] dx, dy = Bx - Ax, By - Ay dsq = dx*dx + dy*dy #if dsq==0:dsq=0.0001 #-I am such a hack... dr = sqrt(dsq) #ignore own force if dr > scn.MAXINTERAD: continue #ignore if beyond interaction radius # add acceleration/force from this glob #print(globA.name, ' has a problem with ', globB.name) force = G * globA.tmass * globB.tmass / dsq tacc[0] += force*dx/dr tacc[1] += force*dy/dr tloc[0] += tvel[0] * scn.DAMP #DAMPVELx tloc[1] += tvel[1] * scn.DAMP #DAMPVELy tvel[0] += tacc[0] * scn.DAMP #DAMPACCx tvel[1] += tacc[1] * scn.DAMP #DAMPACCy ###!!!maybe add a mock relativity here by # damping acceleration as approaches v approaches c #-check if over max velocity, if so set to max if tvel[0] > scn.MAXVEL: tvel[0] = scn.MAXVEL if tvel[0] < -scn.MAXVEL: tvel[0] = -scn.MAXVEL if tvel[1] > scn.MAXVEL: tvel[1] = scn.MAXVEL if tvel[1] < -scn.MAXVEL: tvel[1] = -scn.MAXVEL tacc = [0.0, 0.0, 0.0] #-merge if close enough (relative to draw size - a cheat - I know) if dr <= (globA.scale[0]+globB.scale[0])*scn.DSIZE: if scn.ACCRETE: #-they're close, merge smaller to larger if globA.tmass >= globB.tmass: globMerge(globB, globA) else: globMerge(globA, globB) ###!!!also add collision damping here based on density if scn.EXPLODE: vx1, vy1 = globA.vel[0], globA.vel[1] vx2, vy2 = globB.vel[0], globB.vel[1] vdx, vdy = vx2 = vx1, vy2 - vy1 vdsq =vdx*vdx + vdy*vdy vdr = sqrt(vdsq) if abs(vdr) > scn.EXTHRESH: if globA.tmass >= globB.tmass: if globB.type != 'META': addExplosivo(globB, vdx, vdy) else: if globA.type != 'META': addExplosivo(globA, vdx, vdy) #update loc, vel, and insert kframes globA.location[0] = tloc[0] #/ scn.SPACEsSCALE globA.location[1] = tloc[1] #/ scn.SPACESsCALE globA.vel = tvel #if globA.name[0:len(SPREFIX)] != SPREFIX: # ignore vectails of merged globs if globA.merged == False: globA.keyframe_insert('tmass') globA.keyframe_insert('merged') if globA.bhole == False: globA.keyframe_insert('location', 0) globA.keyframe_insert('location', 1) globA.keyframe_insert('vel', 0) globA.keyframe_insert('vel', 1) #-check if the glob has vectails, update if they exist for c in globA.children: if c.name[0:7] == 'vectail': updateTailFromVec(globA) continue # vectail updated, so leave loop def loopit(): t1 = time.ctime(time.time()) #GI = makeGlobIndex() scn.frame_current = scn.STARTFRAME #-adjust for user changes to vectails updateAllVecFromTail() setCurvesToConstant() #-start on frame 2, 1st should NOT be recalculated for f in range(scn.STARTFRAME+1, scn.STARTFRAME+scn.FRAMERUN): scn.frame_current = f print(scn.frame_current) #runit(GI) runit() t2 = time.ctime(time.time()) # calc processing time s1, m1 = int(t1[17:19]), int(t1[14:16]) s2, m2 = int(t2[17:19]), int(t2[14:16]) print(str(t1)[11:20], str(t2)[11:20]) print('simulation time: ' + str(abs(m2-m1)) + ':' + str(abs(s2-s1))) ###!!!ABSORB SOME IMPACT IN COLLISION!!! ### THESEARE NOT INDESTRUCTABLE... ### ALSO MAYBE ADD POTENTIAL FOR 'GLANCING BLOW'S and 'BOUNCES' ### because for now w acrete and axplode off all collisions will pass thru def globMerge(globS, globL): print(globL.name, globL.tmass, '+', globL.tmass, globS.name) #if globS.bhole == 1: return globS.merged = True #-make merged globs vectail disappear for cs in globS.children: if cs.name[0:7] == 'vectail': cs.scale = [0.0, 0.0, 0.0] cs.keyframe_insert('scale', 0) cs.keyframe_insert('scale', 1) #-figure out vector between L and S to offset # constraint of S on L as if stuck where hit # also to direct particles/explode modifier SLx = globS.location[0] - globL.location[0] SLy = globS.location[1] - globL.location[1] if SLx >= globL.scale[0]*scn.DSIZE: SLx = globL.scale[0]*scn.DSIZE if SLy >= globL.scale[0]*scn.DSIZE: SLy = globL.scale[0]*scn.DSIZE if SLx < -globL.scale[0]*scn.DSIZE: SLx = -globL.scale[0]*scn.DSIZE if SLy < -globL.scale[0]*scn.DSIZE: SLy = -globL.scale[0]*scn.DSIZE #SLx = 1 ###!!! sun merging is still weird #SLy = 1 #-contrain smaller glob to larger gcon = globS.constraints.new('COPY_LOCATION') gcon.use_offset = True gcon.target = globL #-add constraint influence keyframe of # pre-merge state to previous frame ftemp = scn.frame_current scn.frame_current = ftemp - 1 gcon.influence = 0 gcon.keyframe_insert('influence') #-back to this frame to insert merged state keyframe scn.frame_current = ftemp gcon.influence = 1 gcon.keyframe_insert('influence') globS.location = [SLx, SLy, 0.0] #-add keyframes for post-merge # location and constraint influence globS.keyframe_insert('location', 0) globS.keyframe_insert('location', 1) # add velocity ofsmall glob to larger ### ??? ### CHECK THIS, OBJECT VELS SEEM TO GOOF ON IMPACK newvx = (globL.vel[0] * globL.tmass + globS.vel[0] * globS.tmass) / (globL.tmass + globS.tmass) newvy = (globL.vel[1] * globL.tmass + globS.vel[1] * globS.tmass) / (globL.tmass + globS.tmass) #-update larger globs total mass and keyframe globL.tmass += globS.tmass globL.keyframe_insert('tmass') print('mass updated : ', globL.name, globL.tmass) globL.vel[0] = newvx globL.vel[1] = newvy def setCurvesToConstant(): for ob in scn.objects: if ob.glob: oanim = ob.animation_data if oanim: ocurves = oanim.action.fcurves for ocurve in ocurves: ocurve.extrapolation = 'CONSTANT' ###analysis fxns def calcTotalMassRemaing(): #also how many last past 500 frames or so... pass ######################### USER INTERFACE ######################### def initSceneProps(scn): bpy.types.Scene.FRAMERUN = bpy.props.IntProperty( name = "framerun") bpy.types.Scene.PARTICLES = bpy.props.IntProperty( name = "particles") bpy.types.Scene.M = bpy.props.FloatProperty( name = "earthmass") bpy.types.Scene.T = bpy.props.FloatProperty( name = "siderealyear") bpy.types.Scene.WIDTH = bpy.props.IntProperty( name = "width") bpy.types.Scene.HEIGHT = bpy.props.IntProperty( name = "height") bpy.types.Scene.CLIP = bpy.props.IntProperty( name = "clip") bpy.types.Scene.DSIZE = bpy.props.FloatProperty( name = "drawsize") #bpy.types.Scene.SOLDIAM = bpy.props.FloatProperty( # name = "earthdiameter") bpy.types.Scene.A = bpy.props.FloatProperty( name = "spacespace") bpy.types.Scene.SIZEVAR = bpy.props.FloatProperty( name = "sizevar") bpy.types.Scene.DAMP = bpy.props.FloatProperty( name = "damp") bpy.types.Scene.LOOPATCLIP = bpy.props.BoolProperty( name = "loopatclip") bpy.types.Scene.MAXVEL = bpy.props.FloatProperty( name = "maxiumum velocity") bpy.types.Scene.SOLDIAM = bpy.props.FloatProperty( name = "sun radius") bpy.types.Scene.SUNRANDLOC = bpy.props.BoolProperty( name = "random sun location") bpy.types.Scene.RANDVEL = bpy.props.BoolProperty( name = "random starting velocity") bpy.types.Scene.STARTFRAME = bpy.props.IntProperty( name = "startframe") bpy.types.Scene.VECMULT = bpy.props.FloatProperty( name = "vectordisplaymult") bpy.types.Scene.MAXINTERAD = bpy.props.FloatProperty( name = "maxinteractionradius") bpy.types.Scene.RANDVELRANGE = bpy.props.FloatProperty( name = "rendvelrange") bpy.types.Scene.EXPLODE = bpy.props.BoolProperty( name = "explode on impact") bpy.types.Scene.ACCRETE = bpy.props.BoolProperty( name = "accrete or not") bpy.types.Scene.METABALLS = bpy.props.BoolProperty( name = "accrete or not") bpy.types.Scene.EXTHRESH = bpy.props.FloatProperty( name = "explodeimpacethreshold") scn.LOOPATCLIP = LOOPATCLIP scn.FRAMERUN = FRAMERUN scn.PARTICLES = PARTICLES scn.M = M scn.T = T scn.WIDTH = WIDTH scn.HEIGHT = HEIGHT scn.CLIP = CLIP scn.DSIZE = DSIZE #scn.SOLDIAM = SOLDIAM scn.SIZEVAR = SIZEVAR scn.DAMP = DAMP scn.MAXVEL = MAXVEL #scn.SOLDIAM = SOLDIAM scn.SUNRANDLOC = SUNRANDLOC scn.RANDVEL = RANDVEL scn.STARTFRAME = STARTFRAME scn.RANDVELRANGE = RANDVELRANGE scn.MAXINTERAD = MAXINTERAD scn.EXTHRESH = EXTHRESH scn.A = A scn.VECMULT = VECMULT scn.ACCRETE = ACCRETE scn.EXPLODE = EXPLODE scn.METABALLS = METABALLS initSceneProps(scn) bpy.types.Object.glob = bpy.props.BoolProperty( name = 'glob', default = False) bpy.types.Object.tmass = bpy.props.FloatProperty( name = 'total mass of object including others merged to it.', default = 0.0) bpy.types.Object.vel = bpy.props.FloatVectorProperty( name = 'velocity', default = (0.0, 0.0, 0.0)) bpy.types.Object.merged = bpy.props.BoolProperty( name = 'merged', default = False) bpy.types.Object.bhole = bpy.props.BoolProperty( name = 'blackhole', default = False) class OBJECT_PT_SOMPanel(bpy.types.Panel): bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" bl_context = "physics" bl_label = "Simple Orbital Mechanics" def draw_header(self, context): layout = self.layout def draw(self, context): layout = self.layout scene = scn row1 = layout.row(align=True) row2 = layout.row(align=True) split1 = row1.split(percentage=0.5) colX = split1.column() colY = split1.column() split2 = row2.split(percentage=0.33) colL = split2.column() colM = split2.column() colR = split2.column() colX.label(text="2D Gravity/Accretion Simulator", icon='WORLD_DATA') ltext1 = 'vx: ' + str(scn.objects.active.vel[0])[0:9] + ' | ' + 'vy: ' + str(scn.objects.active.vel[1])[0:9] colY.label(text=ltext1) colX.label(text=scn.objects.active.name) colY.label(text='scale: ' + str(scn.objects.active.scale[0])) ltext2 = 'tmass: ' + str(scn.objects.active.tmass)[0:7] ltext3 = 'merged: ' + str(scn.objects.active.merged) colX.label(text=ltext3) colY.label(text=ltext2) colX.label(text='bhole: ' + str(scn.objects.active.bhole)) colY.label(text='') colX.operator("create_globfield") colY.operator("delete_globfield") colX.label(text = '') colY.operator("clear_animation") colX.operator("addVecTails") colY.operator("setVecTanToNearestSun") colX.prop(scene, 'RANDVEL', icon='BLENDER', toggle=True) colY.prop(scene, 'LOOPATCLIP', icon='BLENDER', toggle=True) colX.prop(scene, 'RANDVELRANGE', icon='BLENDER')#, toggle=True) colY.prop(scene, 'VECMULT', icon='BLENDER')#, toggle=True) colL.operator("animate_globfield") colM.prop(scene, 'FRAMERUN') colR.prop(scene, 'STARTFRAME') colL.prop(scene, 'WIDTH', icon='BLENDER')#, toggle=True) colM.prop(scene, 'HEIGHT', icon='BLENDER')#, toggle=True) colR.prop(scene, 'CLIP', icon='BLENDER')#, toggle=True) colL.separator() colM.separator() colR.separator() colL.prop(scene, 'PARTICLES', icon='BLENDER')#, toggle=True) colM.prop(scene, 'MAXINTERAD', icon='BLENDER')#, toggle=True) colR.prop(scene, 'SIZEVAR', icon='BLENDER')#, toggle=True) colL.label(text='') colM.prop(scene, 'DSIZE', icon='BLENDER')#, toggle=True) #colR.prop(scene, 'SOLDIAM', icon='BLENDER')#, toggle=True) colL.separator() colM.separator() colR.separator() colL.prop(scene, 'DAMP', icon='BLENDER')#, toggle=True, expand=False) #colM.prop(scene, 'GRAVITY', icon='BLENDER')#, toggle=True) colR.prop(scene, 'MAXVEL', icon='BLENDER')#, toggle=True) sunBox = colL.box() sunBox.label(text = "Sun/Blackhole") sunBox.operator("create_sunhole") sunBox.operator("delete_sunholes") sunBox.prop(scene, 'SOLDIAM', icon='BLENDER')#, toggle=True) sunBox.prop(scene, 'SUNRANDLOC', icon='BLENDER', toggle=True) #sunBox.prop(scene, 'SUNsDSIZE', icon='BLENDER')#, toggle=True) #colM.template_curve_mapping(scene, 'mycurve', type='VECTOR') #colR.template_reports_banner() # these would be nice, also remember to useevent = true on props colM.prop(scene, 'EXTHRESH', icon='BLENDER', toggle=True) colR.prop(scene, 'A', icon='BLENDER')#, toggle=True) colM.operator('createMeshPath') colM.operator('testit3') #colR.label(text='') colM.label(text='') colR.label(text='') colM.operator('testit') colR.operator('testit2') colM.prop(scene, 'EXPLODE', icon='BLENDER', toggle=True) colR.prop(scene, 'ACCRETE', icon='BLENDER', toggle=True) #colM.prop(scene, 'TESTCOLLECT', icon='BLENDER', toggle=True) #colR.menu('amenu', 'irmanu') #colM.prop_enum(scene, 'TESTMENU', 't') #colM.prop_enum(scene, 'TESTMENU', 't') class SCENE_OT_testit3(bpy.types.Operator): bl_idname = "testit3" bl_label = "testit3" bl_options = {'REGISTER'} bl_description = "testit3" def invoke(self, context, event): print('---trying blah---') #tob = scn.objects.active #print(tob.name) #createMetaBall() setCurvesToConstant() return("FINISHED") class SCENE_OT_testit(bpy.types.Operator): bl_idname = "testit" bl_label = "testit" bl_options = {'REGISTER'} bl_description = "testit" def invoke(self, context, event): print('---trying to create a metaglob---') createSolSystem() return("FINISHED") class SCENE_OT_testit2(bpy.types.Operator): bl_idname = "testit2" bl_label = "testit2FLUSH" bl_options = {'REGISTER'} bl_description = "testit2" def invoke(self, context, event): print('---testit2---') flush() #tob = scn.objects.active #print(tob.name) #createMetaBall() return("FINISHED") class SCENE_OT_createMeshPath(bpy.types.Operator): bl_idname = "createMeshPath" bl_label = "createMeshPath" bl_options = {'REGISTER'} bl_description = "create mesh ribbon from glob paths" def invoke(self, context, event): print('---watch it grow---') createMeshPath() return("FINISHED") class SCENE_OT_setVecTanToNearestSun(bpy.types.Operator): bl_idname = "setVecTanToNearestSun" bl_label = "setVectorTangentToSuns" bl_options = {'REGISTER'} bl_description = "set vectors tangent to their nearest sun" def invoke(self, context, event): print('---turn away from the light---') setVecTangentToNearestHole() return("FINISHED") class SCENE_OT_addVecTails(bpy.types.Operator): bl_idname = "addVecTails" bl_label = "addVectorTails" bl_options = {'REGISTER'} bl_description = "add/update vector tails to globs" def invoke(self, context, event): print('---youve got tails!---') addVectorTails() return("FINISHED") class SCENE_OT_delete_sunholes(bpy.types.Operator): bl_idname = "delete_sunholes" bl_label = "deleteSunHoles" bl_options = {'REGISTER'} bl_description = "remove all suns" def invoke(self, context, event): print('---goodbye norma jean---') delSuns() return("FINISHED") class SCENE_OT_create_globfield(bpy.types.Operator): bl_idname = "clear_animation" bl_label = "clearAnimation" bl_options = {'REGISTER'} bl_description = "clear all animation data" def invoke(self, context, event): print('---be gone animation data!---') clearAnimation() return("FINISHED") class SCENE_OT_create_sunhole(bpy.types.Operator): bl_idname = "create_sunhole" bl_label = "createSunHole" bl_options = {'REGISTER'} bl_description = "create a Sun/Blackhole type stationary gravity well" def invoke(self, context, event): print('---mmm... matter---') makeSunHole() return("FINISHED") class SCENE_OT_create_globfield(bpy.types.Operator): bl_idname = "create_globfield" bl_label = "createGlobs" bl_options = {'REGISTER'} bl_description = "create a field of globs based on parameters" def invoke(self, context, event): print('---let there be globs---') makeGlobField() return("FINISHED") class SCENE_OT_delete_globfield(bpy.types.Operator): bl_idname = "delete_globfield" bl_label = "deleteGlobs" bl_description = "delete all globs" bl_options = {'REGISTER', 'UNDO'} def invoke(self, context, event): print('---destroy all globs---') delGlobs() return{'FINISHED'} class SCENE_OT_animate_globfield(bpy.types.Operator): bl_idname = "animate_globfield" bl_label = "animateGlobs" bl_description = "gravitometicically animateify globjects" bl_options = {'REGISTER', 'UNDO'} def invoke(self, context, event): print('---i am busy calculating glob animations---') loopit() bpy.context.scene.frame_current = STARTFRAME return{'FINISHED'} def register(): bpy.types.Scene.some_strvar = bpy.props.StringProperty( name='some_strvar', description='a string variable i might need') def unregister(): del bpy.types.Scene.some_strvar if __name__ == "__main__": register() ########## DROPDOWN BOX / MENU """ class RENDER_PT_matdropdown(bpy.types.Panel): bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" bl_context = "render" bl_label = "Material Dropdown lists" def draw(self, context): rd = context.scene layout = self.layout layout.prop(rd, "mat_list_old", text="Replace") layout.prop(rd, "mat_list_new", text="by") layout.separator() row = layout.row() row.operator("custom.update_materiallists") row.operator("custom.print_materials") def replace(): MATERIALS = [] for i in range(len(bpy.data.materials)): MATERIALS.append((str(i), bpy.data.materials[i].name, str(i))) bpy.types.Scene.EnumProperty( attr="mat_list_old", name="Replace", description="Choose a material to be replaced", items = MATERIALS, default = '0') bpy.types.Scene.EnumProperty( attr="mat_list_new", name="Replace by", description="Choose a replacement material", items = MATERIALS, default = '1') class CUSTOM_OT_update_materiallists(bpy.types.Operator): bl_idname = "CUSTOM_OT_update_materiallists" bl_label = "Update" bl_description = "Update the dropdown boxes. Necessary if you added or deleted materials in the scene." def invoke(self, context, event): replace() return{'FINISHED'} class CUSTOM_OT_print_materials(bpy.types.Operator): bl_idname = "CUSTOM_OT_print_materials" bl_label = "Print" bl_description = "Print the selections of the dropdown boxes." def invoke(self, context, event): old = bpy.data.materials[int(bpy.context.scene.mat_list_old)] new = bpy.data.materials[int(bpy.context.scene.mat_list_new)] print("Replace",old.name,"by",new.name) return{'FINISHED'} if __name__ == '__main__': bpy.types.register(CUSTOM_OT_update_materiallists) bpy.types.register(CUSTOM_OT_print_materials) replace() bpy.types.register(RENDER_PT_matdropdown) """