Blender includes 'math' and 'os' modules. THE REQUIRED Blender->jME AXIS FLIP is -90 around X Axis. Scripts which use arg params should discard sys.argv up to and including "--". Can run Python interactively in Blender by opening a Python window and using menu Scripts / System / Interactive... It has good command-line recall! Has no line folding. Truncates long lines. :( The only real problem is that the entire session gets zapped whenever you Ctrl-Z in the View window. :( TEXT EDITOR Must use CTRL-SHIFT-C and CTRL-SHIFT-V to use system clipboard. UPDATE MENUS In any script window Scripts / Update Menus. This will not update an already loaded module. Once a module is loaded into memory, it is there for that Blender run. APPLYING TRANSFORMS. Example tm for TransformMatrix To vertexes: v.co * tm To another transform: o.matrixLocal * tm (for inplace: o.matrixLocal *= tm) tm may be a rotation matrix created like: RotationMatrix(5,4,'z') or obtained by any node like o.matrixLocal.copy(). If you want to "see" the change, remember to Blender.Redraw() ONLY RotationAngles are constructed with UNIT DEGREES. !!! N.B.!!! Euler constructor takes RADIANS, not Degress as stated in API! Most things are in radians. THIS SEQUENCE DOES EXACTLY PLACE AN OBJECT (except for Dim, which I guess is bounds???) ******************************************************************************* EXTREME INCONSISTENCY IN Radians vs. Degrees Matrix <--> Euler conversions regardless whether units are Rads or Degrees. Object.rot is in RADIAN UNITS. Transformation matrix multiplication only works with DEGREE UNITS. Example. To rotate object o 30 degrees around the Y axis: o.matrixLocal = o.matrixLocal * Euler(0, 30, 0).toMatrix().resize4x4() and in fact Euler(0, 30, 0).toMatrix().resize4x4() == RotationMatrix(30, 4, 'y') ******************************************************************************* N.b. Active = the SINGLE active item. Sel = possibly multiple selecteds. Absolute vertex location = vertex location transformed by object world location. = Mesh.MVert.co * Object.Object.mat # Why ignore Mesh.MVert.no direction? Angles generally in radians. type(BlenderObject) just returns Can get the action for Armature Objects, but I have no idea what to do with it. Local == relative to parent TODO Can go both directions from loc + euler rot <--> quaternine. How does Object.Object.color show up in the Blender Gui, as a material??? s.new('name', 'file/path.wav') for images, texts, sounds, fonts. s.new('name') other I GUESS THESE INSTANTIATE AND ADD TO THE BlockSeq, but VERIFY! VERIFY bpy.data.* These generally return a X.X type, e.g. Object.Object. VERIFY THAT o.getEuler() == o.rot IN RADIANS o.getLocation() == o.loc o.getMatrix() == o.mat o.getName() == o.name o.getSize() == o.size o.getType() == o.type o.getParent() == o.parent o.isSelected() == o.sel o.select() == o.sel = o.getAllProperties() == o.gameProperties o.loc == o.matrixLocal[3][0:3] FIND OUT DEFAULT colbits value .tag attributes are for indicating that elements are being used within a loop. Wrapped/Unwrapped objects. In this doc, I use @ to indicate Wrapped data. Wrapped objects are refs to objects used by Blender directly. Can make an unwrapped copy of a wrapped object by using the constructor with the wrapped object. IDIOMS # How to make a new mesh? actImg = bpy.data.images.active actObj = bpy.data.scenes.active.objects.active Window.EditMode(0) # Should generally get or new meshes in Object mode objMesh = actobj.getData(mesh=True) OR aMesh = bpy.data.meshes.new('aname') # Populate the mesh me.verts.extend(coords) me.verts.extend(faces) # also adds edges me.delete(...) bpy.data.scenes.active.objects.new(aMesh, 'aname') Apply an image to mesh faces (texface) aMesh.faceUV = True img = bpy.data.images.new('aname', width, height) # or seq.new('name', 'file/path.wav') for f in aMes.faces: f.image = img Window.RedrawAll() blenderObject.getType() Modules * = new style No! Blender.NMesh Deprecated bpy Window Window.RedrawAll() curmode = Window.EditMode() Window.EditMode(0|1) Blender old Blender.Save(filename, overwrite=0) File type determined by extension. Can only save ALL program data! bpy.data.* These generally return a X.X type, e.g. Object.Object. class libBlockSeq. CRITICAL See dedicated section below. newMesh = bpy.data.meshes.new('myMesh') N.b. that Scene.Scene objects hold their own seq. of Objects though, and this is where Objects are instantiated: sce.objecs.new(amesh) Blender.Mesh Blender.BGL OpenGL constants and functions. All functs start with "gl". Blender.Draw windowing interface + keyboard/mouse codes Draw.ESCKEY, Draw.QKEY, Draw.LEFTMOUSE,... Draw.Redraw(1) (Use this for Draw iface, not Blender.Redraw et. al.) Draw.Exit() Draw.Text("printf format %s"[, varargs...,][, "fontsize"]) Draw.Create(val) creates a button? (could be a scroller or slider?) Draw.Register(screenDrawHandler, inputEventHandler, buttonEventHalder) where def screenDrawHandler():... def inputEventHandler(evtNum, press) # press 1==press, 0==rel. def buttonEventHandler(btnNumber) # Btn creator defs evt ints Looks like the Draw.Button() object is for the obvious purpose, and Draw.Create() doesn't take positional params? Use Draw.PupMenu(str) for pop-ups with no menu!. If 2nd param empty, no menu drawn. Drawing geometry: Blender.BGL.getColor3f(r,g,b) # Set painting color Blender.BGL.glRectf(x1, y2, x2, y2) # N.b. coords a bottom left, so # Y goes UP class bpy.data.libBlockSeq. These are sequences, which are a supertype of List. See following sect. Fetch a libBlockSeq: s = bpy.data.*s['itemname'] # Can't set this way!!! from ext file: s = bpy.data.*s['itemname', '//fname.blend'|None] activeItem = s.active (says only applies to images, scenes, texts. Not objects????) s.new('name', 'file/path.wav') for images, texts, sounds, fonts. s.new('name') other I GUESS THESE INSTANTIATE AND ADD TO THE BlockSeq, but VERIFY! s.unlink(scene|group|text) Contrary to the supported lists above, scene.objects says it supports .link(ob), .unlink(ob), new(obdata) Sequence Dictionary. len(s) works Iterator. "for x [not] in" works -> List. list(s) class Object.Object !!!CRITICAL!!! Note that after parenting with P, "N", loc, scale reflect world transform! relative it is these + static-matrixParentInverse (which is set when parented) N.b. if you manually parent, the object is MOVED to be relative to the origin, and no matrixParentInverse is set! getLocation(),getSize(),getEuler() all relative to object itself by default. Pass param 'worldspace' for the obvious purpose. .getData(name_only=False, mesh=False) Set mesh for Mesh (a.o.t. NMesh) Gets data from before entering Edit mode (commits when exit Edit mode??) Should generally get or new meshes in Object mode .getEuler(space='localspace') (or 'worldspace') .getEuler() == .rot N.B. getEuler().toQuat() WILL GIVE WRONG ANSWER because that requires deg. .getLocation(space='localspace') Exactly equal to ob.mat[3][0:3] for worldspace .getMaterials() Returns Object's material list for either Object or Object Data (according to .colbits) @.getMatrix(space='worldspace') I think wrapped only for worldspace? .getSize(space='localspace') .getType(space='localspace') .select(True|False) .setEuler(e); setLocation(x,y,z); setSize(x,y,z); // all Local .setName(n); setMaterials(m); @.getBoundBox(worldspace=1) .getAllProperties() .link(dataBlock) ~ .shareFrom(object) .boundingBox .colbits Set like "...+(1<<5)...". Set=Object mat; Unset=Obj Data mat. .color (= jME defaultColor) .gameProperties .lib .loc, WORLD, === exactly what View N shows for LotX/Y/X .mat (== .matrixWorld!!!! = .matrix) .matrixLocal (relative to parent) YOU GET A POINTER TO THIS. Therefore use .copy()!!! N.b. both mat and matrixLocal.rotationPart().toEuler() give DEGREES .name .parent .parentType .rb* Rigid Body settings @euler .rot, WORLD, == exactly what View N shows for RotX/Y/X conv. to rads RADIAN UNITS!!! N.B. getEuler().toQuat() WILL GIVE WRONG ANSWER because that requires deg. WORKAROUND is mat.toQuat() which == mat.rotationPart().toEuler().toQuat(). .sel .size, WORLD, === exactly what View N shows for ScaleX/Y/X .type .matrixParentInverse. CRITICAL for nested objects. See above. ASSERTION: child.matrixLocal == child.mat * child.matrixParentInverse WORK-AROUND to get Euler in degress is x.matrix*.rotationPart().toEuler() class Mesh.Mesh transform(matrix, recalc_normals=False, selected_only=False) recalcNormals(direction=0) .lib path to blend file (or None) .edges extend/delete this .materials .name .sel (WO) [de]selects all veritices, edges, and faces .faces extend/delete this .verts extend/delete this boolean .faceUV UV-mapped texture faces with face.uv [Vector?(u1,v1),...] each ver. boolean .vertexUV Sticky UV-mapping, each vertex vert.uvco = Vector(u,v) int .mode bitmap of Blender.Mesh.Modes dictionary, incl. TWOSIDED (n.b. NOT!!! Blender.Material.Modes) Blender ignores TWOSIDED and always colors both sides!??? class Scene.Scene .getName() .setName(n) .makeCurrent() .update() Object.Object seq .getChildren() Object.Object getActiveObject() .[un]link(object) .lib .name .objects .objects.active, .objects.context (visible), objects.selected class Mesh.MVert @co vector no unit normal vector sel class Mesh.MFace Mcol[] .col (may be null) .mat index into mesh's material list .mode if mesh.UV boolean.smooth averages vertex normals (TODO: test whether this leaves everything else along and can toggle back and forth) vector[] .uv MVert[] .verts class Mathutils.Quaternion looks great. class Matrix Fill all of top row, then second row, etc., like new Matrix([first, row], [second, row],...) .rotationPart() includes the scale, so that m.rotationPart().scalePart() works, but m.rotationPart().translationPart() does not; and also 3x3matrix.scalePart() is ok, but 3x3matrix.translationPart() is not. 4x4matrix.toEuler() == 4.4matrix.rotationPart().toEuler(). matrix.toQuat() == matrix.rotationPart().toEuler().toQuat() Where I = identity matrix of 0 w/ 1s on diagonal: I = I.invert() m * I == m Class Material float[3] .mirCol float[3] .rgbCol ==> jME .diffuse float[3] .specCol ==> jME .specular # These from Shaders tab. DO VERIFY float .ref shader reflections float spec (0-2?) ==> jME .shininess int .hard (1-511?) specularity hardness ==> ? low .hard makes the specular spread much larger float .emit (0-1). Amt of DIFFUSE to emit.. jME.emissive = .diffuse * .emit float .amb (0-1) ".1 to .5 is good". Amount of global ambient it receives jME.ambient = (1,1,1,1) * (.amb, .amb, .amb, 1) Ambient light is uneffected by shading. The Ambient color is ENTIRELY dependent upon the World ambient colors. World ambient color of Black entirely eliminates any ambience. int[] .enabledTextures float .glossMir refl. gloss float .glossTra refract. gloss int .mode key of Blender.Material.Modes dict (but setMode() takes a str) (n.b. NOT!!! Blender.Mesh.Modes) (Modes is some unusual kind of Str->int dict. .keys() strs, .values() ints. But can't iter*() these directly. Example to list all: for m in matModes.keys(): print str(matModes[m])+ ": " + m Example to display Strs for all set modes: : if material.mode & matModes[m]: print m Example to add a mode: mat.mode = mat.mode | matModes['RAYMIRROR'] E.g. plain color seems to set it to SHADELESS/4. mode of VCOL_LIGHT, VCOL_PAINT seems -> jME .colorMaterial .name MTex[] .textures [Mirror settings seem to have no effect by default. All mirror settings seem to have to do with either Ray Mirroring or Fresnel Mirroring. Easiest way to see mirror color is to use button panel, Mirror Transp., Ray Mirror ON + turn up RayMir. Programatically: m.mirCol, m.rayMirr (0-1), m.mode = m.mode | matModes['RAYMIRROR']] Class Armature Bone{} .bones boolean .envelope .name boolean .vertexGroups Class Bone Bone[] .children .deform_dist I think for envelopes mode .head['ARMATURESPACE'] .head['BONESPACE'] = localSpace .matrix['ARMATURESPACE'] .matrix['BONESPACE'] = localSpace // I think need to calculate .matrix +/- .head .name Bone .parent float .weight .headRadius, .tailReadius // for envelope mode SKELETAL ANIMATION Channel = possibly None IPO. Channel names correspond to vertex groups or bones (a.o.t. IP names). action = armatureObject.getAction() key frame numbers: action.getFrameNumbers() action.getAllChannelIpos() is a hash of channel names -> Ips classes. (as noted channel name != IP name) (This contains the ipoCurves) Mesh.key fields: blocks, relative (bool), type (from ..., value (float) (.key not set for my keyframe animation mesh) Mesh.getVertsFromGroup(groupName, 1) # If get too many returned, try 3rd param blenderObject.getPose().bones = PoseBonesDict (contrary to API spec) blenderObject.evaluatePose(f) NOTES frames 1, 10, 21 Channel Names: 'Head', 'Bod' object.clrParent() just detaches from parent, it doesn't delete anything. o.clrParent(2) updates o's transforms to incorporate parent's and stay still. There is a bug where need to access one (or more) of Blender obj: .loc, .rot, .getLocation('worldspace'), .getEuler('worldspace), or changes made to parent, even with o.clrParent(2) will not adhere. MENU REGISTRATION Just need to add the #!BPY header + the registration string. Registration string must contain 4 lines with comment Name: 'the name' Blender: 241 Group: 'MenuID' Value of Group is concatenated menu names, like "Render" or "Add/Mesh". [EXCEPT File / Export is just Export? Perhaps File is always excluded?) If the menu/submenu allows customization, your script will be added with your "Name" alphabetically among the other scripts in that menu/submenu. MAKING CHANGES VISIBLE bpy.data.scenes.active.update(1) may be necessary to update scene first. Blender.Redraw() only works when the 3D View is visible. probably same for Blender.Window.RedrawAll()? CURSOR. Blender.Window.WaitCursor(0|1); Comparing blender items. It seems that completely equivalent items are not == equivalent; and even for wrapped data, vertex variables obtained from mesh and face, for instance are "is" inequal. Looks like "==" will work for all purposes where one would expect to use "is". == behavior of these items exactly matches uniqueness in sets. ARMATURES/ANIMATION armaObj.getAction() Not arma.getAction()! armaObj.getPose() Not arma.getPose()! arma.bones != armaObj.getPose().bones. Latter are PoseBones instances. Set rotations of pose bones at keyframes like so: pbone.quat = ... Adds to quat ipo (Quaternion rotation, CHANGE [from what, 0?]) (pbone.loc = ... (Quaternion location, CHANGE [from what, 0?]) pbone.size = ... (Scale, CHANGE [from what?])) pbone.insertKey(armaObj, frameNum, Object.Pose.ROT) list(bpy.data.actions) == Blender.Armature.NLA.GetActions().values EXC. SEQUENCE BONE WEIGHTING for groupName in BlenderMesh.getVertGroupNames(): if groupName not in boneNames: continue try w = BlenderMesh.getVertsFromGroup(groupName, 1, [bMVert.index])[0][1] except IndexError: continue WRITE WEIGHT weightAssignments += 1 weightSum += weight Referencing face.uv definitely THROWs if mesh.faceUV == False Referencing face.col definitely THROWs if mesh.vertexColors == False Referencing vert.uvco definitely THROWs if mesh.vertexUV == False mesh.faceUV == mesh UV Texture layer present mesh.vertexColors == Vertex Color layer present mesh.vertexUV == The one Sticky layer is present