First script preview! Add PolySphere

Hey guys,

Current version 0.1.6 Updated on 4th March 2012

http://img.photobucket.com/albums/v299/metalliandy/PolySphere_Th.jpg
http://projects.blender.org/tracker/download.php/153/467/26250/19583/add_mesh_polysphere.py

This is just a preview of my first attempt at making a script!

I thought it would be cool to create a PolySphere for sculpting and because its all quads, you wont get the pinching issue at the poles :slight_smile:

I want to go much further with this by enabling the creation of different resolutions of the polysphere and also add some other primitives that are optimised for sculpting.

Im a little stuck with a few errors and if anyone could give me a hand that would be great :slight_smile:

If i go through the add menu and add the polysphere I get this error

Traceback (most recent call last):
  File "C:\PROGRA~1\BLENDE~1\Blender2.5\2.55\scripts\addons\add_mesh_polysphere.py", line 91, in execute
    Add_PolySphere(self, context)
NameError: global name 'Add_PolySphere' is not defined

location:<unknown location>:-1


and if i enable the add on through the prefs, it automatically adds the mesh in the 3d window, which is obviously not very ideal :stuck_out_tongue:

Here is the code i have so far

# ##### 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####

bl_addon_info = {
    "name": "Add PolySphere",
    "author": "Andy Davies (metalliandy)",
    "version": (0,1),
    "blender": (2, 5, 5),
    "api": 33866,
    "location": "View3D > Add > Mesh > PolySphere",
    "description": "Adds a PolySphere (all quads) for sculpting",
    "warning": "",
    "wiki_url": "",
    "tracker_url": "",
    "category": "Object"}
    

import bpy

#Add Cube to scene
bpy.ops.mesh.primitive_cube_add(view_align=False, enter_editmode=False, location=(0, 0, 0), rotation=(0, 0, 0), layers=(True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False))

#Changes name of Cube to polysphere adds variable cube
cube = bpy.context.object
cube.name = "PolySphere"

#Positions Cube primitive to scene centre
bpy.context.active_object.location = [0, 0, 0]


#Adds Subsurf Modifier
bpy.ops.object.modifier_add(type='SUBSURF')


#Selects Subsurf Modifier for editing
subsurf = cube.modifiers['Subsurf']

    
#Changes Subsurf levels
subsurf.levels = 3  

#Applys Subsurf Modifier
bpy.ops.object.modifier_apply(apply_as='DATA', modifier="Subsurf")


#Adds smooth shading
bpy.ops.object.shade_smooth()


#Change to Editmode
bpy.ops.object.editmode_toggle()


#Selects cube in Editmode
bpy.ops.mesh.select_all(action='TOGGLE')


#Adds transform "To Sphere"
bpy.ops.transform.tosphere(value=1, mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False, snap_normal=(0, 0, 0), release_confirm=False)


#Change to Objectmode
bpy.ops.object.editmode_toggle()


#makes PolySphere an operator
class AddPolySphere(bpy.types.Operator):
    
    """Add a PolySphere."""
    bl_idname = "mesh.primitive_polysphere_add"
    bl_label = "Add PolySphere"
    bl_options = {'REGISTER', 'UNDO'}


    def execute(self, context):
        Add_PolySphere(self, context)

        return {'FINISHED'}


# Register the operator
def menu_func(self, context):
    self.layout.operator(AddPolySphere.bl_idname, text="PolySphere", icon='PLUGIN')


def register():
    # Add "PolySphere" menu to the "Add Mesh" menu.
    bpy.types.INFO_MT_mesh_add.append(menu_func)


def unregister():
    # Remove "PolySphere" menu from the "Add Mesh" menu.
    bpy.types.INFO_MT_mesh_add.remove(menu_func)

if __name__ == "__main__":
    register()

Any ideas?

Thanks for looking :slight_smile:

This is not a solution but it works:

# ##### 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####

bl_addon_info = {
	"name": "Add PolySphere",
	"author": "Andy Davies (metalliandy)",
	"version": (0,1),
	"blender": (2, 5, 5),
	"api": 33866,
	"location": "View3D > Add > Mesh > PolySphere",
	"description": "Adds a PolySphere (all quads) for sculpting",
	"warning": "",
	"wiki_url": "",
	"tracker_url": "",
	"category": "Object"}
    

import bpy



#makes PolySphere an operator
class AddPolySphere(bpy.types.Operator):
    
	"""Add a PolySphere."""
	bl_idname = "mesh.primitive_polysphere_add"
	bl_label = "Add PolySphere"
	bl_options = {'REGISTER', 'UNDO'}


	def execute(self, context):
		#Add Cube to scene
		bpy.ops.mesh.primitive_cube_add(view_align=False, enter_editmode=False, location=(0, 0, 0), rotation=(0, 0, 0), layers=(True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False))

		#Changes name of Cube to polysphere adds variable cube
		cube = bpy.context.object
		cube.name = "PolySphere"

		#Positions Cube primitive to scene centre
		bpy.context.active_object.location = [0, 0, 0]


		#Adds Subsurf Modifier
		bpy.ops.object.modifier_add(type='SUBSURF')


		#Selects Subsurf Modifier for editing
		subsurf = cube.modifiers['Subsurf']

			
		#Changes Subsurf levels
		subsurf.levels = 3  

		#Applys Subsurf Modifier
		bpy.ops.object.modifier_apply(apply_as='DATA', modifier="Subsurf")


		#Adds smooth shading
		bpy.ops.object.shade_smooth()


		#Change to Editmode
		bpy.ops.object.editmode_toggle()


		#Selects cube in Editmode
		bpy.ops.mesh.select_all(action='TOGGLE')


		#Adds transform "To Sphere"
		bpy.ops.transform.tosphere(value=1, mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False, snap_normal=(0, 0, 0), release_confirm=False)


		#Change to Objectmode
		bpy.ops.object.editmode_toggle()


		return {'FINISHED'}


# Register the operator
def menu_func(self, context):
	self.layout.operator(AddPolySphere.bl_idname, text="PolySphere", icon='PLUGIN')


def register():
	# Add "PolySphere" menu to the "Add Mesh" menu.
	bpy.types.INFO_MT_mesh_add.append(menu_func)


def unregister():
	# Remove "PolySphere" menu from the "Add Mesh" menu.
	bpy.types.INFO_MT_mesh_add.remove(menu_func)

if __name__ == "__main__":
	register()

Edit:
Oh, and thank you for the polysphere. Very useful for sculpting. Now i don’t have to import it from zbrush :wink:

ok. this is rough, but works and allows you to keep the execute part of the code clean :slight_smile:

#
#  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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####

bl_addon_info = {
    "name": "Add PolySphere",
    "author": "Andy Davies (metalliandy)",
    "version": (0,1),
    "blender": (2, 5, 5),
    "api": 33866,
    "location": "View3D > Add > Mesh > PolySphere",
    "description": "Adds a PolySphere (all quads) for sculpting",
    "warning": "",
    "wiki_url": "",
    "tracker_url": "",
    "category": "Object"}
    

import bpy

def Add_PolySphere():
    #Add Cube to scene
    bpy.ops.mesh.primitive_cube_add(view_align=False, enter_editmode=False, location=(0, 0, 0), rotation=(0, 0, 0), layers=(True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False))

    #Changes name of Cube to polysphere adds variable cube
    cube = bpy.context.object
    cube.name = "PolySphere"

    #Positions Cube primitive to scene centre
    bpy.context.active_object.location = [0, 0, 0]

    #Adds Subsurf Modifier
    bpy.ops.object.modifier_add(type='SUBSURF')

    #Selects Subsurf Modifier for editing
    subsurf = cube.modifiers['Subsurf']

    #Changes Subsurf levels
    subsurf.levels = 3  

    #Applys Subsurf Modifier
    bpy.ops.object.modifier_apply(apply_as='DATA', modifier="Subsurf")

    #Adds smooth shading
    bpy.ops.object.shade_smooth()

    #Change to Editmode
    bpy.ops.object.editmode_toggle()

    #Selects cube in Editmode
    bpy.ops.mesh.select_all(action='TOGGLE')

    #Adds transform "To Sphere"
    bpy.ops.transform.tosphere(value=1, mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False, snap_normal=(0, 0, 0), release_confirm=False)

    #Change to Objectmode
    bpy.ops.object.editmode_toggle()


#makes PolySphere an operator
class AddPolySphere(bpy.types.Operator):
    
    """Add a PolySphere."""
    bl_idname = "mesh.primitive_polysphere_add"
    bl_label = "Add PolySphere"
    bl_options = {'REGISTER', 'UNDO'}


    def execute(self, context):
        Add_PolySphere()

        return {'FINISHED'}


# Register the operator
def menu_func(self, context):
    self.layout.operator(AddPolySphere.bl_idname, text="PolySphere", icon='PLUGIN')


def register():
    # Add "PolySphere" menu to the "Add Mesh" menu.
    bpy.types.INFO_MT_mesh_add.append(menu_func)


def unregister():
    # Remove "PolySphere" menu from the "Add Mesh" menu.
    bpy.types.INFO_MT_mesh_add.remove(menu_func)

if __name__ == "__main__":
    register()

Thanks for the replies guys :slight_smile:

@Josip Kladaric
My pleasure :slight_smile:
This is exactly why i thought it would be cool to make the script! :smiley:

@zeffii,
Thanks :slight_smile:

I changed the Add_PolySphere to AddPolySphere :slight_smile:

When i changed it i get another error lol

Traceback (most recent call last):
  File "add_mesh_polysphere.t", line 91, in execute
TypeError: bpy_struct.__new__() takes exactly 1 argument (2 given)

location:<unknown location>:-1

Im assuming this is to do with the (self, context) somehow

I also noticed that i forgot to define the add polysphere at the start too it seems

def Add_PolySphere():

I have seen this sort of thing in other scripts…is it needed?

Thanks for all the help, this stuff is a great challenge but i have much to learn ^^

inside the class AddPolySphere, the function called execute(…) calls Add_PolySphere()
Add_PolySphere, doesnt take arguments, in this case it doesn’t strictly need to.

run the version of your script that i modified, python relies on whitespace to establish what lines of code are part of what functions (functions definitions are def name_here():)

def ThisFunction():
    some line of code
    some other line of code
    last line of this functions code

def ThisOtherFunction():
    some other line of code
    some other other line of code
    last line of this function

def Nifty(somevariable):
    use somevariable to do something
    ThisFunction()
    ThisOtherFunction()

Nifty(somevariable)

With python you must be careful with white-spaces and indentation, there are a lot of other small (seemingly trivial) things that can take you a long time to figure out. Even if you are learning python purely to use with blender it will benefit you to do a bit of mundane unrelated-to-blender python programming (maybe a few dedicated hours is enough!) to have a more fundamental understanding of the subtle syntax.

Imagine you plan to move to a country where the native language is not one that you already speak, it is a good idea to learn some vocabulary first. syntax is less important for natural languages, because people can see gestures and expressions. computer languages rely heavily on correct syntax for the things you are trying to express to make sense to the ‘interpreter’. Programming languages and natural languages both need to make sense of the inputted code. For programming you’ll have noticed correct/valid syntax alone is not enough, just like you can have a nice completely unintelligible sentence without warnings in your word processor.

so… i think you will have more fun with blender-python after doing some unrelated exercises. A recommendation : http://code.google.com/edu/languages/google-python-class/index.html this shows you how to read data from a file, and extract data from files into usable structures ( ie, you can apply this to vertices…and how they wire up to make a full mesh )

Thanks zeffii!
I didnt realise that you edited you previous post just before i made my last one :slight_smile:

I will start reading up :slight_smile:

Thanks again for all the help :smiley:

no problem :slight_smile: keep at it!

edit to the above post. where i say whitespace , i also mean to include ‘indentation’ both are supremely important in python

take a simple cube, have it selected.
type these lines into the console:


>>> import bpy
>>> obj = bpy.context.active_objects
>>> for v in obj.data.vertices: print(v.co)

Behold you will see a collection of Vector((…, …, …)) these are the x,y,z coordinates of each vertex. The order in which they appear is used for numbering. It allows us to string them together to make a quad (or a tri., which i wont cover)

the list of faces will contain something like [[0,1,2,3],[2,3,4,1],…etc…[6,4,7,5]]
this example uses vertex 0,1 2 3 for the first face in the mesh, and vertex 2,3,4,1 for the next.


import bpy

obj = bpy.context.active_object

# v here means (each) element
for v in obj.data.vertices: 
    print(v.co)
    
for f in obj.data.faces:
    quad = f.vertices
    for vert_index in quad:
        print(vert_index)


now lets say i want to get a better idea of what’s going on…
some of this is going to be a bit over the top.


import bpy

obj = bpy.context.active_object

# v here means (each) element
for v in obj.data.vertices: 
    print(v.co)

print("[")    # prints opening brace
for f in obj.data.faces:
    quad = f.vertices
    index_string = "["
    numcount = 0

    for vert_index in quad:
        index_string+= str(vert_index)
        numcount += 1
        if numcount < 4: index_string+= ","
    index_string+="]"
    print(index_string)
 
print("]")    # prints closing brace


i tried the Zeffi exxample and get nothing
is there another vesion which is working normally?

Thanks and happy 2.5

Thanks again zeffii :slight_smile:

@RickyBlender,
If you enable the addon and add the mesh through the add menu it works great :slight_smile:

what do you mean by add menu ?
where do you find this?
is it with spacebar and slect the script convert ?

i simply added a bezier open curve with 2 points
and the converted mesh was a closed curve instead of being open as the original bezier curve!

Thanks

ricky you are so confusing sometimes. seriously.

metalliandy, look at my last code snippet, if you run that in the texteditor while a cube is selected it should output to the dos/commandline, something like

[
[0,1,2,3]
[4,7,6,5]
[0,4,5,1]
[1,5,6,2]
[2,6,7,3]
[4,0,3,7]
]

Hey guys,
Thanks for the replies :slight_smile:

I have updated the first post with the location of the script and latest version will always be kept at http://www.metalliandy.com/downloads/scripts/add_mesh_polysphere.py

@Ricky,
If you install the addon through the preferences and enable it, or copy and paste the text to the text editor and hit run script, the PolySphere is then available in the Space bar/Shift+A Add menu.

Im not sure what you mean about the bezier curves though. This script should just add a PolySphere at 0, 0 ,0 regardless of anything you have selected or in the scene.

Hope that helps :slight_smile:

@zeffii,
Thats awesome and will be extremely useful when i come to make the other primitives as they require actual modelling.

How would one go about taking the vector and face positions from the console to create a mesh?

Thanks for all the help zeffii! serious! :smiley:

metalliandy, i help you because in doing so it helps me understand this stuff better too :slight_smile:

See this code from the previous thread. Some of these steps i have now done for you superficially but in order for you to ‘get it’ you will want to figure out some things on your own. the google code link from earlier is a big fat hint for a place to start learning the basics of python. When i started python, it took me a week to get through those exercises.

Because making a mesh from a list of verts and faces is such a common requirement in 3d engines, the Blender API provides a neat function specifically to do just that. All we have to do is pass it a list of faces (in the form of vertex_indices) and verts and let blender do the rest. The tricky bit is the lists need to be formatted in a certain way, thats where mundane python exercises are useful to demystify data structures and operations on those structures.

# create new mesh structure
mesh = bpy.data.meshes.new("Relief")
mesh.from_pydata(Verts, [], Faces)
mesh.update()

# create an object from this mesh
new_object = bpy.data.objects.new("Ascii Data", mesh)
new_object.data = mesh

# adding object called "Ascii Data" to the scene
scene = bpy.context.scene
scene.objects.link(new_object)

# deals with selecting
scene.objects.active = new_object
new_object.select = True

in your script you probably notice that several lines are quite long. bpy.ops.mesh.primitive_cube_add(…)
you might be feeding the primitive_cube_add function a lot more information than it needs.
some of the information ‘reported by the console’ is redundant because if the function doesnt receive certain parameters explicitly from the user then it will use the default values. ( the console reporting function displays the default values too… whereas for you to get the functionality you want, you can go for bpy.ops.mesh.primitive_cube_add() in this scenario )

experiment with that function and others by dropping some of the parameters that you are passing… and get an understanding of what they are for, why you might use them and if they are crucial to what you want.

in the pdf/zip here : http://blenderartists.org/forum/showthread.php?t=193908 you will
see a script called meshes.py ( iirc) things should start to make sense.

:slight_smile:
Thanks for the link…the pdf will get a good long read tonight!

I have been getting my info from the info panel in order to get the code for every action and copied that into the text window.
I will go through and take a look at what can be shorted tonight :slight_smile:

The next step for this script will be me figuring out how to add multiple resolutions to the polysphere and adding a menu, though im not sure if this will be possible as after applying the Subsurf happens before the final object is created.

I may have to break the script into parts and then find a way to have a menu appear before the creation part of the script is initiated and then use the different parts of the script depending on what is chosen.

Thanks again!

Breaking your code up into smaller sections is a good way to make it easier to read (and thus…debug). The UI element of the Blender API seems to have stabilized lately, i’m eager to also learn a bit more about that.

as for subdivision, people can use the multiresolution modifier (and it’s simple enough to increase subsurf manually…)

yea :slight_smile:
The thing is that i need to subsurf the cube before the “To Sphere” transform can be used, in order to get the sphere, but perhaps the current res is ok for most uses.
I just though it would be a nice addition if people wanted a lower res PolySphere for some reason…maybe its not needed after all?

Maybe its possible to include a switch that will jump to a predefined part of the script and execute it from there?

For now, perhaps it would be a better idea to try and workout how to get some other shapes implemented first.
So many choices :stuck_out_tongue:

i like this sphere model cause you almost have squares all around
which looks like something asked before for doing a Disco ball!

happy 2.5

Hey guys,
I have just updated the the script with a few minor changes.

I cleaned the code a little and now the PolySphere is created at the default size of 2 units.

http://www.metalliandy.com/downloads/scripts/add_mesh_polysphere.py

:slight_smile: