Содержание блога
Скрипт на Python основан на туториале 2D сетка (the 2D grid ). Ставится плагин из архива: mitsubishi_logo_blender_python.zip . Проект на C# и OpenTK для VS: MitsubishiLogo_OpenTkOpenGL30CSharp.zip В Blender'е появляется кнопка "Create Logo" на вкладке N-панели. Созданный по кнопке логотип можно экспортировать в Collada (.dae) формат и загрузить в C# OpenGL 3 с помощью следующего кода, который находится в одном файле "Program.cs".
Генерация модели в Blender 2.8
__init__.py
Python 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
bl_info = {
"name" : "mitsubishi_logo_blender_python" ,
"author" : "Ivan Enzhaev" ,
"description" : "" ,
"blender" : ( 2 , 80 , 0 ) ,
"version" : ( 0 , 0 , 1 ) ,
"location" : "View3D" ,
"warning" : "" ,
"category" : "Generic"
}
import bpy
from . my_op import My_Operator
from . my_panel import My_PT_Panel
classes = ( My_Operator, My_PT_Panel)
register, unregister = bpy.utils .register_classes_factory ( classes)
my_op.py
Python 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import bpy
from math import pi, sin, cos
# http://sinestesia.co/blog/tutorials/python-2d-grid/
# https://blender3d.com.ua/meshi-s-pomoshchyu-python-i-blender-2d-setka/
# Settings
name = "Gridtastic"
rows = 5
columns = 10
size = 0.5
# Utility functions
def vert( column, row) :
""" Create a single vert """
return ( column * size, row * size, 0 )
def face( column, row) :
""" Create a single face """
return ( column * rows + row,
( column + 1 ) * rows + row,
( column + 1 ) * rows + 1 + row,
column * rows + 1 + row)
class My_Operator( bpy.types .Operator ) :
bl_idname = "view3d.create_2d_grid"
bl_label = "Simple operator"
bl_description = "Create Mitsubishi Logo"
def execute( self , context) :
# verts = [(0, 0, 0)]
# verts = [(x, 0, 0) for x in range(columns)]
# verts = [(x, y, 0) for x in range(columns) for y in range(rows)]
# faces = [(0, 5, 6, 1)]
# Looping to create the grid
#verts = [vert(x, y) for x in range(columns) for y in range(rows)]
#faces = [face(x, y) for x in range(columns - 1) for y in range(rows - 1)]
r1 = 1
r2 = r1 * sin( 60 * pi / 180 ) * 2
verts = [ ]
faces = [ ]
x0 = 0
y0 = 0
verts.append ( ( x0, y0, 0 ) )
face_index = 0
for main in range ( 90 , 360 , 120 ) :
low_offset = main - 30
high_offset = main + 30
x1 = round ( r1 * cos( low_offset * pi / 180 ) , 3 )
y1 = round ( r1 * sin( low_offset * pi / 180 ) , 3 )
x2 = round ( r2 * cos( main * pi / 180 ) , 3 )
y2 = round ( r2 * sin( main * pi / 180 ) , 3 )
x3 = round ( r1 * cos( high_offset * pi / 180 ) , 3 )
y3 = round ( r1 * sin( high_offset * pi / 180 ) , 3 )
verts.append ( ( x1, y1, 0 ) )
verts.append ( ( x2, y2, 0 ) )
verts.append ( ( x3, y3, 0 ) )
f0 = 0
face_index += 1
f1 = face_index
face_index += 1
f2 = face_index
face_index += 1
f3 = face_index
faces.append ( ( f0, f1, f2, f3) )
# Create Mesh Datablock
mesh = bpy.data .meshes .new ( name)
mesh.from_pydata ( verts, [ ] , faces)
# Create Object and link to scene
obj = bpy.data .objects .new ( name, mesh)
bpy.context .scene .collection .objects .link ( obj)
# Select the object
bpy.context .view_layer .objects .active = obj
bpy.context .active_object .select_set ( state= True )
return { 'FINISHED' }
my_panel.py
Python 1
2
3
4
5
6
7
8
9
10
11
12
13
import bpy
class My_PT_Panel( bpy.types .Panel ) :
bl_idname = "My_Panel"
bl_label = "Mitsubishi Logo"
bl_category = "Mitsubishi Logo"
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
def draw( self , context) :
layout = self .layout
row = layout.row ( )
row.operator ( "view3d.create_2d_grid" , text= "Create Logo" )
Загрузка модели на C# и рисование на шейдерном OpenGL 3
Program.cs
C# 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
using System ;
using OpenTK.Graphics.OpenGL ;
using OpenTK.Graphics ;
using OpenTK ;
using System.Xml ;
namespace MitsubishiLogoFromDae
{
class Program
{
static void Main( string [ ] args)
{
using ( var window = new Window( ) )
{
window. Title = "Mitsubishi" ;
window. Run ( ) ;
}
}
}
class Window : GameWindow
{
private Matrix4 _projMatrix;
private Matrix4 _modelMatrix;
private Matrix4 _mpMatrix;
private int _uMPMatrixLocation;
private int _amountOfVertices = 0 ;
public Window( ) : base ( 250 , 250 , new GraphicsMode( 32 , 0 , 0 , 8 ) ) { }
protected override void OnLoad( EventArgs e)
{
base . OnLoad ( e) ;
var vShaderSource =
@"
#version 130
in vec3 aPosition;
uniform mat4 uMPMatrix;
void main()
{
gl_Position = uMPMatrix * vec4(aPosition, 1.0);
}
" ;
var fShaderSource =
@"
#version 130
precision mediump float;
out vec4 fragColor;
void main()
{
fragColor = vec4(0.0);
}
" ;
var vShader = GL. CreateShader ( ShaderType. VertexShader ) ;
GL. ShaderSource ( vShader, vShaderSource) ;
GL. CompileShader ( vShader) ;
var fShader = GL. CreateShader ( ShaderType. FragmentShader ) ;
GL. ShaderSource ( fShader, fShaderSource) ;
GL. CompileShader ( fShader) ;
var program = GL. CreateProgram ( ) ;
GL. AttachShader ( program, vShader) ;
GL. AttachShader ( program, fShader) ;
GL. LinkProgram ( program) ;
GL. UseProgram ( program) ;
int vbo;
GL. CreateBuffers ( 1 , out vbo) ;
float [ ] positions;
LoadData( "Assets/Models/Logo.dae" , out positions) ;
_amountOfVertices = positions. Length / 3 ;
GL. BindBuffer ( BufferTarget. ArrayBuffer , vbo) ;
GL. BufferData ( BufferTarget. ArrayBuffer , sizeof ( float ) * positions. Length , positions, BufferUsageHint. StaticDraw ) ;
var aPositionLocation = GL. GetAttribLocation ( program, "aPosition" ) ;
GL. VertexAttribPointer ( aPositionLocation, 3 , VertexAttribPointerType. Float , false , 0 , 0 ) ;
GL. EnableVertexAttribArray ( aPositionLocation) ;
_uMPMatrixLocation = GL. GetUniformLocation ( program, "uMPMatrix" ) ;
GL. ClearColor ( 1f, 1f, 1f, 1f) ;
}
protected override void OnRenderFrame( FrameEventArgs e)
{
base . OnRenderFrame ( e) ;
GL. Clear ( ClearBufferMask. ColorBufferBit ) ;
_modelMatrix =
Matrix4. CreateScale ( 5f, 5f, 1f) *
Matrix4. CreateTranslation ( 0f, 0f, - 1f) ;
_mpMatrix = _modelMatrix * _projMatrix;
GL. UniformMatrix4 ( _uMPMatrixLocation, false , ref _mpMatrix) ;
GL. DrawArrays ( PrimitiveType. Triangles , 0 , _amountOfVertices) ;
SwapBuffers( ) ;
}
protected override void OnResize( EventArgs e)
{
base . OnResize ( e) ;
GL. Viewport ( 0 , 0 , Width, Height) ;
float aspect = ( float ) Width / Height;
float worldWidth = aspect * 20f;
_projMatrix = Matrix4. CreateOrthographic ( worldWidth, 20f, 100f, - 100f) ;
}
private void LoadData( string path, out float [ ] vertices)
{
XmlDocument xml = new XmlDocument( ) ;
xml. Load ( path) ;
XmlNamespaceManager xnm = new XmlNamespaceManager( xml. NameTable ) ;
xnm. AddNamespace ( "a" , "http://www.collada.org/2005/11/COLLADASchema" ) ;
XmlElement root = xml. DocumentElement ;
XmlNode pNode = root. SelectSingleNode ( "//a:p" , xnm) ;
int [ ] p = Array. ConvertAll ( pNode. InnerText . Split ( new char [ ] { ' ' } ) , int . Parse ) ;
XmlNode posNode = root. SelectSingleNode ( "//a:float_array[substring(@id, string-length(@id) - string-length('mesh-positions-array') + 1) = 'mesh-positions-array']" , xnm) ;
float [ ] positions = Array. ConvertAll ( posNode. InnerText . Split ( new char [ ] { ' ' } ) , float . Parse ) ;
vertices = new float [ 3 * p. Length / 2 ] ;
int triangleIndex = 0 ;
for ( int i = 0 ; i < p. Length ; i++ )
{
if ( i % 2 == 0 )
{
vertices[ triangleIndex++ ] = positions[ p[ i] * 3 ] ;
vertices[ triangleIndex++ ] = positions[ p[ i] * 3 + 1 ] ;
vertices[ triangleIndex++ ] = positions[ p[ i] * 3 + 2 ] ;
}
}
}
}
}
Код из серии туториалов по генерации мешей на Blender 2.7 и 2.9
1.
Part 1: The 2D Grid (перевод:
2D сетка )
Blender 2.7
Python 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import bpy
# Settings
name = 'Gridtastic'
rows = 5
columns = 10
size = 1
# Utility functions
def vert( column, row) :
""" Create a single vert """
return ( column * size, row * size, 0 )
def face( column, row) :
""" Create a single face """
return ( column* rows + row,
column * rows + 1 + row,
( column + 1 ) * rows + 1 + row,
( column + 1 ) * rows + row)
# Looping to create the grid
verts = [ vert( x, y) for x in range ( columns) for y in range ( rows) ]
faces = [ face( x, y) for x in range ( columns - 1 ) for y in range ( rows - 1 ) ]
# Create Mesh Datablock
mesh = bpy.data .meshes .new ( name)
mesh.from_pydata ( verts, [ ] , faces)
# Create Object and link to scene
obj = bpy.data .objects .new ( name, mesh)
bpy.context .scene .objects .link ( obj)
# Select the object
bpy.context .scene .objects .active = obj
obj.select = True
Blender 2.9
Python 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import bpy
# Settings
name = 'Gridtastic'
rows = 5
columns = 10
size = 1
# Utility functions
def vert( column, row) :
""" Create a single vert """
return ( column * size, row * size, 0 )
def face( column, row) :
""" Create a single face """
return ( column* rows + row,
( column + 1 ) * rows + row,
( column + 1 ) * rows + 1 + row,
column * rows + 1 + row)
# Looping to create the grid
verts = [ vert( x, y) for x in range ( columns) for y in range ( rows) ]
faces = [ face( x, y) for x in range ( columns - 1 ) for y in range ( rows - 1 ) ]
# Create Mesh Datablock
mesh = bpy.data .meshes .new ( name)
mesh.from_pydata ( verts, [ ] , faces)
# Create Object and link to scene
obj = bpy.data .objects .new ( name, mesh)
bpy.context .scene .collection .objects .link ( obj)
# Select the object
bpy.context .view_layer .objects .active = obj
obj.select = True
2.
Part 2: Cubes and Matrices (перевод:
Кубики и матрицы )
Blender 2.7
Python 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import bpy
import math
from mathutils import Matrix
# -----------------------------------------------------------------------------
# Settings
name = 'Cubert'
# Origin point transformation settings
mesh_offset = ( 0 , 0 , 0 )
origin_offset = ( 0 , 0 , 0 )
# Matrices settings
translation = ( 0 , 0 , 0 )
scale_factor = 1
scale_axis = ( 1 , 1 , 1 )
rotation_angle = math .radians ( 0 )
rotation_axis = 'X'
# -----------------------------------------------------------------------------
# Utility Functions
def vert( x, y, z) :
""" Make a vertex """
return ( x + origin_offset[ 0 ] , y + origin_offset[ 1 ] , z + origin_offset[ 2 ] )
# -----------------------------------------------------------------------------
# Cube Code
verts = [ vert( 1.0 , 1.0 , -1.0 ) ,
vert( 1.0 , -1.0 , -1.0 ) ,
vert( -1.0 , -1.0 , -1.0 ) ,
vert( -1.0 , 1.0 , -1.0 ) ,
vert( 1.0 , 1.0 , 1.0 ) ,
vert( 1.0 , -1.0 , 1.0 ) ,
vert( -1.0 , -1.0 , 1.0 ) ,
vert( -1.0 , 1.0 , 1.0 ) ]
faces = [ ( 0 , 1 , 2 , 3 ) ,
( 4 , 7 , 6 , 5 ) ,
( 0 , 4 , 5 , 1 ) ,
( 1 , 5 , 6 , 2 ) ,
( 2 , 6 , 7 , 3 ) ,
( 4 , 0 , 3 , 7 ) ]
# -----------------------------------------------------------------------------
# Add Object to Scene
mesh = bpy.data .meshes .new ( name)
mesh.from_pydata ( verts, [ ] , faces)
obj = bpy.data .objects .new ( name, mesh)
bpy.context .scene .objects .link ( obj)
bpy.context .scene .objects .active = obj
obj.select = True
# -----------------------------------------------------------------------------
# Offset mesh to move origin point
obj.location = [ ( i * -1 ) + mesh_offset[ j] for j, i in enumerate ( origin_offset) ]
# -----------------------------------------------------------------------------
# Matrix Magic
translation_matrix = Matrix.Translation ( translation)
scale_matrix = Matrix.Scale ( scale_factor, 4 , scale_axis)
rotation_mat = Matrix.Rotation ( rotation_angle, 4 , rotation_axis)
obj.matrix_world *= translation_matrix * rotation_mat * scale_matrix
# -----------------------------------------------------------------------------
# Matrix Magic (in the mesh)
# Uncomment this to change the mesh
# obj.data.transform(translation_matrix * scale_matrix)
Blender 2.9
Python 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import bpy
import math
from mathutils import Matrix
# -----------------------------------------------------------------------------
# Settings
name = 'Cubert'
# Origin point transformation settings
mesh_offset = ( 0 , 0 , 0 )
origin_offset = ( 0 , 0 , 0 )
# Matrices settings
translation = ( 0 , 0 , 0 )
scale_factor = 1
scale_axis = ( 1 , 1 , 1 )
rotation_angle = math .radians ( 0 )
rotation_axis = 'X'
# -----------------------------------------------------------------------------
# Utility Functions
def vert( x, y, z) :
""" Make a vertex """
return ( x + origin_offset[ 0 ] , y + origin_offset[ 1 ] , z + origin_offset[ 2 ] )
# -----------------------------------------------------------------------------
# Cube Code
verts = [ vert( 1.0 , 1.0 , -1.0 ) ,
vert( 1.0 , -1.0 , -1.0 ) ,
vert( -1.0 , -1.0 , -1.0 ) ,
vert( -1.0 , 1.0 , -1.0 ) ,
vert( 1.0 , 1.0 , 1.0 ) ,
vert( 1.0 , -1.0 , 1.0 ) ,
vert( -1.0 , -1.0 , 1.0 ) ,
vert( -1.0 , 1.0 , 1.0 ) ]
faces = [ ( 0 , 1 , 2 , 3 ) ,
( 4 , 7 , 6 , 5 ) ,
( 0 , 4 , 5 , 1 ) ,
( 1 , 5 , 6 , 2 ) ,
( 2 , 6 , 7 , 3 ) ,
( 4 , 0 , 3 , 7 ) ]
# -----------------------------------------------------------------------------
# Add Object to Scene
mesh = bpy.data .meshes .new ( name)
mesh.from_pydata ( verts, [ ] , faces)
obj = bpy.data .objects .new ( name, mesh)
bpy.context .scene .collection .objects .link ( obj)
bpy.context .view_layer .objects .active = obj
obj.select = True
# -----------------------------------------------------------------------------
# Offset mesh to move origin point
obj.location = [ ( i * -1 ) + mesh_offset[ j] for j, i in enumerate ( origin_offset) ]
# -----------------------------------------------------------------------------
# Matrix Magic
translation_matrix = Matrix.Translation ( translation)
scale_matrix = Matrix.Scale ( scale_factor, 4 , scale_axis)
rotation_mat = Matrix.Rotation ( rotation_angle, 4 , rotation_axis)
obj.matrix_world @= translation_matrix @ rotation_mat @ scale_matrix
# -----------------------------------------------------------------------------
# Matrix Magic (in the mesh)
# Uncomment this to change the mesh
# obj.data.transform(translation_matrix @ scale_matrix)
3.
Part 3: Icospheres (перевод:
Икосферы )
Blender 2.7
Python 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import bpy
from math import sqrt
# -----------------------------------------------------------------------------
# Settings
scale = 1
subdiv = 5
name = 'Icosomething'
# -----------------------------------------------------------------------------
# Functions
middle_point_cache = { }
def vertex( x, y, z) :
""" Return vertex coordinates fixed to the unit sphere """
length = sqrt( x**2 + y**2 + z**2 )
return [ ( i * scale) / length for i in ( x, y, z) ]
def middle_point( point_1, point_2) :
""" Find a middle point and project to the unit sphere """
# We check if we have already cut this edge first
# to avoid duplicated verts
smaller_index = min ( point_1, point_2)
greater_index = max ( point_1, point_2)
key = '{0}-{1}' .format ( smaller_index, greater_index)
if key in middle_point_cache:
return middle_point_cache[ key]
# If it's not in cache, then we can cut it
vert_1 = verts[ point_1]
vert_2 = verts[ point_2]
middle = [ sum ( i) /2 for i in zip ( vert_1, vert_2) ]
verts.append ( vertex( *middle) )
index = len ( verts) - 1
middle_point_cache[ key] = index
return index
# -----------------------------------------------------------------------------
# Make the base icosahedron
# Golden ratio
PHI = ( 1 + sqrt( 5 ) ) / 2
verts = [
vertex( -1 , PHI, 0 ) ,
vertex( 1 , PHI, 0 ) ,
vertex( -1 , -PHI, 0 ) ,
vertex( 1 , -PHI, 0 ) ,
vertex( 0 , -1 , PHI) ,
vertex( 0 , 1 , PHI) ,
vertex( 0 , -1 , -PHI) ,
vertex( 0 , 1 , -PHI) ,
vertex( PHI, 0 , -1 ) ,
vertex( PHI, 0 , 1 ) ,
vertex( -PHI, 0 , -1 ) ,
vertex( -PHI, 0 , 1 ) ,
]
faces = [
# 5 faces around point 0
[ 0 , 11 , 5 ] ,
[ 0 , 5 , 1 ] ,
[ 0 , 1 , 7 ] ,
[ 0 , 7 , 10 ] ,
[ 0 , 10 , 11 ] ,
# Adjacent faces
[ 1 , 5 , 9 ] ,
[ 5 , 11 , 4 ] ,
[ 11 , 10 , 2 ] ,
[ 10 , 7 , 6 ] ,
[ 7 , 1 , 8 ] ,
# 5 faces around 3
[ 3 , 9 , 4 ] ,
[ 3 , 4 , 2 ] ,
[ 3 , 2 , 6 ] ,
[ 3 , 6 , 8 ] ,
[ 3 , 8 , 9 ] ,
# Adjacent faces
[ 4 , 9 , 5 ] ,
[ 2 , 4 , 11 ] ,
[ 6 , 2 , 10 ] ,
[ 8 , 6 , 7 ] ,
[ 9 , 8 , 1 ] ,
]
# -----------------------------------------------------------------------------
# Subdivisions
for i in range ( subdiv) :
faces_subdiv = [ ]
for tri in faces:
v1 = middle_point( tri[ 0 ] , tri[ 1 ] )
v2 = middle_point( tri[ 1 ] , tri[ 2 ] )
v3 = middle_point( tri[ 2 ] , tri[ 0 ] )
faces_subdiv.append ( [ tri[ 0 ] , v1, v3] )
faces_subdiv.append ( [ tri[ 1 ] , v2, v1] )
faces_subdiv.append ( [ tri[ 2 ] , v3, v2] )
faces_subdiv.append ( [ v1, v2, v3] )
faces = faces_subdiv
# -----------------------------------------------------------------------------
# Add Object to Scene
mesh = bpy.data .meshes .new ( name)
mesh.from_pydata ( verts, [ ] , faces)
obj = bpy.data .objects .new ( name, mesh)
bpy.context .scene .objects .link ( obj)
bpy.context .scene .objects .active = obj
obj.select = True
# -----------------------------------------------------------------------------
# Smoothing
#bpy.ops.object.shade_smooth()
for face in mesh.polygons :
face.use_smooth = True
Blender 2.9
Python 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import bpy
from math import sqrt
# -----------------------------------------------------------------------------
# Settings
scale = 1
subdiv = 5
name = 'Icosomething'
# -----------------------------------------------------------------------------
# Functions
middle_point_cache = { }
def vertex( x, y, z) :
""" Return vertex coordinates fixed to the unit sphere """
length = sqrt( x**2 + y**2 + z**2 )
return [ ( i * scale) / length for i in ( x, y, z) ]
def middle_point( point_1, point_2) :
""" Find a middle point and project to the unit sphere """
# We check if we have already cut this edge first
# to avoid duplicated verts
smaller_index = min ( point_1, point_2)
greater_index = max ( point_1, point_2)
key = '{0}-{1}' .format ( smaller_index, greater_index)
if key in middle_point_cache:
return middle_point_cache[ key]
# If it's not in cache, then we can cut it
vert_1 = verts[ point_1]
vert_2 = verts[ point_2]
middle = [ sum ( i) /2 for i in zip ( vert_1, vert_2) ]
verts.append ( vertex( *middle) )
index = len ( verts) - 1
middle_point_cache[ key] = index
return index
# -----------------------------------------------------------------------------
# Make the base icosahedron
# Golden ratio
PHI = ( 1 + sqrt( 5 ) ) / 2
verts = [
vertex( -1 , PHI, 0 ) ,
vertex( 1 , PHI, 0 ) ,
vertex( -1 , -PHI, 0 ) ,
vertex( 1 , -PHI, 0 ) ,
vertex( 0 , -1 , PHI) ,
vertex( 0 , 1 , PHI) ,
vertex( 0 , -1 , -PHI) ,
vertex( 0 , 1 , -PHI) ,
vertex( PHI, 0 , -1 ) ,
vertex( PHI, 0 , 1 ) ,
vertex( -PHI, 0 , -1 ) ,
vertex( -PHI, 0 , 1 ) ,
]
faces = [
# 5 faces around point 0
[ 0 , 11 , 5 ] ,
[ 0 , 5 , 1 ] ,
[ 0 , 1 , 7 ] ,
[ 0 , 7 , 10 ] ,
[ 0 , 10 , 11 ] ,
# Adjacent faces
[ 1 , 5 , 9 ] ,
[ 5 , 11 , 4 ] ,
[ 11 , 10 , 2 ] ,
[ 10 , 7 , 6 ] ,
[ 7 , 1 , 8 ] ,
# 5 faces around 3
[ 3 , 9 , 4 ] ,
[ 3 , 4 , 2 ] ,
[ 3 , 2 , 6 ] ,
[ 3 , 6 , 8 ] ,
[ 3 , 8 , 9 ] ,
# Adjacent faces
[ 4 , 9 , 5 ] ,
[ 2 , 4 , 11 ] ,
[ 6 , 2 , 10 ] ,
[ 8 , 6 , 7 ] ,
[ 9 , 8 , 1 ] ,
]
# -----------------------------------------------------------------------------
# Subdivisions
for i in range ( subdiv) :
faces_subdiv = [ ]
for tri in faces:
v1 = middle_point( tri[ 0 ] , tri[ 1 ] )
v2 = middle_point( tri[ 1 ] , tri[ 2 ] )
v3 = middle_point( tri[ 2 ] , tri[ 0 ] )
faces_subdiv.append ( [ tri[ 0 ] , v1, v3] )
faces_subdiv.append ( [ tri[ 1 ] , v2, v1] )
faces_subdiv.append ( [ tri[ 2 ] , v3, v2] )
faces_subdiv.append ( [ v1, v2, v3] )
faces = faces_subdiv
# -----------------------------------------------------------------------------
# Add Object to Scene
mesh = bpy.data .meshes .new ( name)
mesh.from_pydata ( verts, [ ] , faces)
obj = bpy.data .objects .new ( name, mesh)
bpy.context .scene .collection .objects .link ( obj)
bpy.context .view_layer .objects .active = obj
obj.select = True
# -----------------------------------------------------------------------------
# Smoothing
#bpy.ops.object.shade_smooth()
for face in mesh.polygons :
face.use_smooth = True
4.
Part 4: A Rounded Cube (перевод: отсутствует)
Blender 2.7
Python 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import bpy
import json
import os
from math import radians
from random import random , uniform
from mathutils import Matrix
# -----------------------------------------------------------------------------
# Functions
def object_from_data( data, name, scene, select = True ) :
""" Create a mesh object and link it to a scene """
mesh = bpy.data .meshes .new ( name)
mesh.from_pydata ( data[ 'verts' ] , data[ 'edges' ] , data[ 'faces' ] )
obj = bpy.data .objects .new ( name, mesh)
scene.objects .link ( obj)
scene.objects .active = obj
obj.select = True
mesh.validate ( verbose= True )
return obj
def transform( obj, position= None , scale= None , rotation= None ) :
""" Apply transformation matrices to an object or mesh """
position_mat = 1 if not position else Matrix.Translation ( position)
if scale:
scale_x = Matrix.Scale ( scale[ 0 ] , 4 , ( 1 , 0 , 0 ) )
scale_y = Matrix.Scale ( scale[ 1 ] , 4 , ( 0 , 1 , 0 ) )
scale_z = Matrix.Scale ( scale[ 2 ] , 4 , ( 0 , 0 , 1 ) )
scale_mat = scale_x * scale_y * scale_z
else :
scale_mat = 1
if rotation:
rotation_mat = Matrix.Rotation ( radians( rotation[ 0 ] ) , 4 , rotation[ 1 ] )
else :
rotation_mat = 1
try :
obj.matrix_world *= position_mat * rotation_mat * scale_mat
return
except AttributeError :
# I used return/pass here to avoid nesting try/except blocks
pass
try :
obj.transform ( position_mat * rotation_mat * scale_mat)
except AttributeError :
raise TypeError ( 'First parameter must be an object or mesh' )
def apply_modifiers( obj, scene, render= False ) :
""" Apply all modifiers on an object """
settings_type = 'PREVIEW' if not render else 'RENDER'
obj.data = obj.to_mesh ( scene, True , settings_type)
obj.modifiers .clear ( )
def set_smooth( obj) :
""" Enable smooth shading on an mesh object """
for face in obj.data .polygons :
face.use_smooth = True
def get_filename( filepath) :
""" Return an absolute path for a filename relative to the blend's path """
base = os .path .dirname ( bpy.context .blend_data .filepath )
return os .path .join ( base, filepath)
# -----------------------------------------------------------------------------
# Using the functions together
def make_object( datafile, name) :
""" Make a cube object """
subdivisions = 0
roundness = 2.5
position = ( uniform( -5 , 5 ) , uniform( -5 , 5 ) , uniform( -5 , 5 ) )
scale = ( 5 * random ( ) , 5 * random ( ) , 5 * random ( ) )
rotation = ( 20 , 'X' )
with open ( datafile, 'r' ) as jsonfile:
mesh_data = json.load ( jsonfile)
scene = bpy.context .scene
obj = object_from_data( mesh_data, name, scene)
transform( obj, position, scale, rotation)
set_smooth( obj)
mod = obj.modifiers .new ( 'Bevel' , 'BEVEL' )
mod.segments = 10
mod.width = ( roundness / 10 ) / ( sum ( scale) / 3 )
if subdivisions > 0 :
mod = obj.modifiers .new ( 'Subdivision' , 'SUBSURF' )
mod.levels = subdivisions
mod.render_levels = subdivisions
#apply_modifiers(obj, scene)
return obj
# -----------------------------------------------------------------------------
# Main code and error control
try :
make_object( get_filename( 'cube.json' ) , 'Rounded Cube' )
except FileNotFoundError as e:
print ( '[!] JSON file not found. {0}' .format ( e) )
except PermissionError as e:
print ( '[!] Could not open JSON file {0}' .format ( e) )
except KeyError as e:
print ( '[!] Mesh data error. {0}' .format ( e) )
except RuntimeError as e:
print ( '[!] from_pydata() failed. {0}' .format ( e) )
except TypeError as e:
print ( '[!] Passed the wrong type of object to transform. {0}' .format ( e) )
Blender 2.9
Python 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import bpy
import json
import os
from math import radians
from random import random , uniform
from mathutils import Matrix
# -----------------------------------------------------------------------------
# Functions
def object_from_data( data, name, scene, select = True ) :
""" Create a mesh object and link it to a scene """
mesh = bpy.data .meshes .new ( name)
mesh.from_pydata ( data[ 'verts' ] , data[ 'edges' ] , data[ 'faces' ] )
obj = bpy.data .objects .new ( name, mesh)
scene.collection .objects .link ( obj)
bpy.context .view_layer .objects .active = obj
obj.select_set ( True )
mesh.validate ( verbose= True )
return obj
def transform( obj, position= None , scale= None , rotation= None ) :
""" Apply transformation matrices to an object or mesh """
position_mat = 1 if not position else Matrix.Translation ( position)
if scale:
scale_x = Matrix.Scale ( scale[ 0 ] , 4 , ( 1 , 0 , 0 ) )
scale_y = Matrix.Scale ( scale[ 1 ] , 4 , ( 0 , 1 , 0 ) )
scale_z = Matrix.Scale ( scale[ 2 ] , 4 , ( 0 , 0 , 1 ) )
scale_mat = scale_x @ scale_y @ scale_z
else :
scale_mat = 1
if rotation:
rotation_mat = Matrix.Rotation ( radians( rotation[ 0 ] ) , 4 , rotation[ 1 ] )
else :
rotation_mat = 1
try :
obj.matrix_world @= position_mat @ rotation_mat @ scale_mat
return
except AttributeError :
# I used return/pass here to avoid nesting try/except blocks
pass
try :
obj.transform ( position_mat @ rotation_mat @ scale_mat)
except AttributeError :
raise TypeError ( 'First parameter must be an object or mesh' )
def apply_modifiers( obj, scene, render= False ) :
""" Apply all modifiers on an object """
bm = bmesh.new ( )
dg = bpy.context .evaluated_depsgraph_get ( )
bm.from_object ( obj, dg)
bm.to_mesh ( obj.data )
bm.free ( )
obj.modifiers .clear ( )
def set_smooth( obj) :
""" Enable smooth shading on an mesh object """
for face in obj.data .polygons :
face.use_smooth = True
def get_filename( filepath) :
""" Return an absolute path for a filename relative to the blend's path """
base = os .path .dirname ( bpy.context .blend_data .filepath )
return os .path .join ( base, filepath)
# -----------------------------------------------------------------------------
# Using the functions together
def make_object( datafile, name) :
""" Make a cube object """
subdivisions = 0
roundness = 2.5
position = ( uniform( -5 , 5 ) , uniform( -5 , 5 ) , uniform( -5 , 5 ) )
scale = ( 5 * random ( ) , 5 * random ( ) , 5 * random ( ) )
rotation = ( 20 , 'X' )
with open ( datafile, 'r' ) as jsonfile:
mesh_data = json.load ( jsonfile)
scene = bpy.context .scene
obj = object_from_data( mesh_data, name, scene)
transform( obj, position, scale, rotation)
set_smooth( obj)
mod = obj.modifiers .new ( 'Bevel' , 'BEVEL' )
mod.segments = 10
mod.width = ( roundness / 10 ) / ( sum ( scale) / 3 )
if subdivisions > 0 :
mod = obj.modifiers .new ( 'Subdivision' , 'SUBSURF' )
mod.levels = subdivisions
mod.render_levels = subdivisions
#apply_modifiers(obj, scene)
return obj
# -----------------------------------------------------------------------------
# Main code and error control
try :
make_object( get_filename( 'cube.json' ) , 'Rounded Cube' )
except FileNotFoundError as e:
print ( '[!] JSON file not found. {0}' .format ( e) )
except PermissionError as e:
print ( '[!] Could not open JSON file {0}' .format ( e) )
except KeyError as e:
print ( '[!] Mesh data error. {0}' .format ( e) )
except RuntimeError as e:
print ( '[!] from_pydata() failed. {0}' .format ( e) )
except TypeError as e:
print ( '[!] Passed the wrong type of object to transform. {0}' .format ( e) )
5.
Part 5: Circles and Cylinders (перевод: отсутствует)
Blender 2.7
Python 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
import bpy
import bmesh
import math
# ------------------------------------------------------------------------------
# Utility Functions
def set_smooth( obj) :
""" Enable smooth shading on an mesh object """
for face in obj.data .polygons :
face.use_smooth = True
def object_from_data( data, name, scene, select = True ) :
""" Create a mesh object and link it to a scene """
mesh = bpy.data .meshes .new ( name)
mesh.from_pydata ( data[ 'verts' ] , data[ 'edges' ] , data[ 'faces' ] )
obj = bpy.data .objects .new ( name, mesh)
scene.objects .link ( obj)
scene.objects .active = obj
obj.select = True
mesh.update ( calc_edges= True )
mesh.validate ( verbose= True )
return obj
def recalculate_normals( mesh) :
""" Make normals consistent for mesh """
bm = bmesh.new ( )
bm.from_mesh ( mesh)
bmesh.ops .recalc_face_normals ( bm, faces= bm.faces )
bm.to_mesh ( mesh)
bm.free ( )
# ------------------------------------------------------------------------------
# Geometry functions
def vertex_circle( segments, z) :
""" Return a ring of vertices """
verts = [ ]
for i in range ( segments) :
angle = ( math .pi *2 ) * i / segments
verts.append ( ( math .cos ( angle) , math .sin ( angle) , z) )
return verts
def face( segments, i, row) :
""" Return a face on a cylinder """
if i == segments - 1 :
ring_start = segments * row
base = segments * ( row + 1 )
return ( base - 1 , ring_start, base, ( base + segments) - 1 )
else :
base = ( segments * row) + i
return ( base, base + 1 , base + segments + 1 , base + segments)
def bottom_cap( verts, faces, segments, cap= 'NGON' ) :
""" Build bottom caps as triangle fans """
if cap == 'TRI' :
verts.append ( ( 0 , 0 , 0 ) )
center_vert = len ( verts) - 1
[ faces.append ( ( i, i+1 , center_vert) ) for i in range ( segments - 1 ) ]
faces.append ( ( segments - 1 , 0 , center_vert) )
elif cap == 'NGON' :
faces.append ( [ i for i in range ( segments) ] )
else :
print ( '[!] Passed wrong type to bottom cap' )
def top_cap( verts, faces, segments, rows, cap= 'NGON' ) :
""" Build top caps as triangle fans """
if cap == 'TRI' :
verts.append ( ( 0 , 0 , rows - 1 ) )
center_vert = len ( verts) - 1
base = segments * ( rows - 1 )
[ faces.append ( ( base+i, base+i+1 , center_vert) )
for i in range ( segments - 1 ) ]
faces.append ( ( segments * rows - 1 , base, center_vert) )
elif cap == 'NGON' :
base = ( rows - 1 ) * segments
faces.append ( [ i + base for i in range ( segments) ] )
else :
print ( '[!] Passed wrong type to top cap' )
# ------------------------------------------------------------------------------
# Main Functions
def make_circle( name, segments= 32 , fill= None ) :
""" Make a circle """
data = {
'verts' : vertex_circle( segments, 0 ) ,
'edges' : [ ] ,
'faces' : [ ] ,
}
if fill:
bottom_cap( data[ 'verts' ] , data[ 'faces' ] , segments, fill)
else :
data[ 'edges' ] = [ ( i, i+1 ) for i in range ( segments) ]
data[ 'edges' ] .append ( ( segments - 1 , 0 ) )
scene = bpy.context .scene
return object_from_data( data, name, scene)
def make_cylinder( name, segments= 64 , rows= 4 , cap= None ) :
""" Make a cylinder """
data = { 'verts' : [ ] , 'edges' : [ ] , 'faces' : [ ] }
for z in range ( rows) :
data[ 'verts' ] .extend ( vertex_circle( segments, z) )
for i in range ( segments) :
for row in range ( 0 , rows - 1 ) :
data[ 'faces' ] .append ( face( segments, i, row) )
if cap:
bottom_cap( data[ 'verts' ] , data[ 'faces' ] , segments, cap)
top_cap( data[ 'verts' ] , data[ 'faces' ] , segments, rows, cap)
scene = bpy.context .scene
obj = object_from_data( data, name, scene)
recalculate_normals( obj.data )
set_smooth( obj)
bevel = obj.modifiers .new ( 'Bevel' , 'BEVEL' )
bevel.limit_method = 'ANGLE'
obj.modifiers .new ( 'Edge Split' , 'EDGE_SPLIT' )
return obj
# ------------------------------------------------------------------------------
# Main Code
#make_circle('Circle', 64)
make_cylinder( 'Cylinder' , 128 , 4 , 'TRI' )
Blender 2.9
Python 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
import bpy
import bmesh
import math
# ------------------------------------------------------------------------------
# Utility Functions
def set_smooth( obj) :
""" Enable smooth shading on an mesh object """
for face in obj.data .polygons :
face.use_smooth = True
def object_from_data( data, name, scene, select = True ) :
""" Create a mesh object and link it to a scene """
mesh = bpy.data .meshes .new ( name)
mesh.from_pydata ( data[ 'verts' ] , data[ 'edges' ] , data[ 'faces' ] )
obj = bpy.data .objects .new ( name, mesh)
scene.collection .objects .link ( obj)
bpy.context .view_layer .objects .active = obj
obj.select = True
mesh.update ( calc_edges= True )
mesh.validate ( verbose= True )
return obj
def recalculate_normals( mesh) :
""" Make normals consistent for mesh """
bm = bmesh.new ( )
bm.from_mesh ( mesh)
bmesh.ops .recalc_face_normals ( bm, faces= bm.faces )
bm.to_mesh ( mesh)
bm.free ( )
# ------------------------------------------------------------------------------
# Geometry functions
def vertex_circle( segments, z) :
""" Return a ring of vertices """
verts = [ ]
for i in range ( segments) :
angle = ( math .pi *2 ) * i / segments
verts.append ( ( math .cos ( angle) , math .sin ( angle) , z) )
return verts
def face( segments, i, row) :
""" Return a face on a cylinder """
if i == segments - 1 :
ring_start = segments * row
base = segments * ( row + 1 )
return ( base - 1 , ring_start, base, ( base + segments) - 1 )
else :
base = ( segments * row) + i
return ( base, base + 1 , base + segments + 1 , base + segments)
def bottom_cap( verts, faces, segments, cap= 'NGON' ) :
""" Build bottom caps as triangle fans """
if cap == 'TRI' :
verts.append ( ( 0 , 0 , 0 ) )
center_vert = len ( verts) - 1
[ faces.append ( ( i, i+1 , center_vert) ) for i in range ( segments - 1 ) ]
faces.append ( ( segments - 1 , 0 , center_vert) )
elif cap == 'NGON' :
faces.append ( [ i for i in range ( segments) ] )
else :
print ( '[!] Passed wrong type to bottom cap' )
def top_cap( verts, faces, segments, rows, cap= 'NGON' ) :
""" Build top caps as triangle fans """
if cap == 'TRI' :
verts.append ( ( 0 , 0 , rows - 1 ) )
center_vert = len ( verts) - 1
base = segments * ( rows - 1 )
[ faces.append ( ( base+i, base+i+1 , center_vert) )
for i in range ( segments - 1 ) ]
faces.append ( ( segments * rows - 1 , base, center_vert) )
elif cap == 'NGON' :
base = ( rows - 1 ) * segments
faces.append ( [ i + base for i in range ( segments) ] )
else :
print ( '[!] Passed wrong type to top cap' )
# ------------------------------------------------------------------------------
# Main Functions
def make_circle( name, segments= 32 , fill= None ) :
""" Make a circle """
data = {
'verts' : vertex_circle( segments, 0 ) ,
'edges' : [ ] ,
'faces' : [ ] ,
}
if fill:
bottom_cap( data[ 'verts' ] , data[ 'faces' ] , segments, fill)
else :
data[ 'edges' ] = [ ( i, i+1 ) for i in range ( segments) ]
data[ 'edges' ] .append ( ( segments - 1 , 0 ) )
scene = bpy.context .scene
return object_from_data( data, name, scene)
def make_cylinder( name, segments= 64 , rows= 4 , cap= None ) :
""" Make a cylinder """
data = { 'verts' : [ ] , 'edges' : [ ] , 'faces' : [ ] }
for z in range ( rows) :
data[ 'verts' ] .extend ( vertex_circle( segments, z) )
for i in range ( segments) :
for row in range ( 0 , rows - 1 ) :
data[ 'faces' ] .append ( face( segments, i, row) )
if cap:
bottom_cap( data[ 'verts' ] , data[ 'faces' ] , segments, cap)
top_cap( data[ 'verts' ] , data[ 'faces' ] , segments, rows, cap)
scene = bpy.context .scene
obj = object_from_data( data, name, scene)
recalculate_normals( obj.data )
set_smooth( obj)
bevel = obj.modifiers .new ( 'Bevel' , 'BEVEL' )
bevel.limit_method = 'ANGLE'
obj.modifiers .new ( 'Edge Split' , 'EDGE_SPLIT' )
return obj
# ------------------------------------------------------------------------------
# Main Code
#make_circle('Circle', 64)
make_cylinder( 'Cylinder' , 128 , 4 , 'TRI' )
Видео урок создания простейшего GUI на Blender 2.7 от Артёма Слаквы
Создание дополнения (аддона) для Blender
VIDEO
Python 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
bl_info = {
"name" : "My First Addon" ,
"category" : "3D View"
}
#Содержит мета-данные аддона, такие как: название, версия, автор...
import bpy
class MyPanel( bpy.types .Panel ) : #Создается класс с типом меню Panel
bl_label = "My Panel" #Название меню
bl_space_type = 'VIEW_3D' #Окно расположения
bl_region_type = 'TOOLS' #Панель расположения
def draw( self , context) : #Функция отображающая содержимое нашего меню
layout = self .layout
#Переменной layout присваивается выражение self.layout
layout.label ( text= "Add mesh:" ) #С помощью label выводится любой текст
split = layout.split ( )
#Переменной split присваивается выражение layout.split()
col = split.column ( align= True )
#Переменной col присваивается выражение split.column(align=True)
col.operator ( "mesh.primitive_cube_add" , text= "Cube" , icon= "MESH_CUBE" )
#Выводится кнопка создания куба с текстом и иконкой
col.operator ( "mesh.primitive_monkey_add" , text= "Monkey" , icon= "MESH_MONKEY" )
#Выводится кнопка создания Сюзанны с текстом и иконкой
def register( ) : #Функция загружает скрипт при включении аддона
bpy.utils .register_class ( MyPanel)
def unregister( ) : #Функция выгружает скрипт при отключении аддона
bpy.utils .unregister_class ( MyPanel)
if __name__ == "__main__" :
register( )
#Функция позволяет запускать скрипт непосредственно из редактора
Скрипт сложения двух чисел
На основе скрипта урока выше (Артёма Слаква) и примера из этого сообщения:
https://stackoverflow.com/a/15610283/4159530 я написал скрипт, который складывает два введённых в поля ввода числа и выводит результат в третье поле, как на прикреплённом скриншоте. Исходники скрипта:
https://rextester.com/MZSSZ58460
main.py
Python 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
bl_info = {
"name" : "Calculator to add two numbers" ,
"category" : "3D View"
}
import bpy
class MyPanel( bpy.types .Panel ) :
bl_label = "Calculator"
bl_space_type = 'VIEW_3D'
bl_region_type = 'TOOLS'
def draw( self , context) :
layout = self .layout
#layout.label(text="Add mesh:")
#split = layout.split()
#col = split.column(align=True)
#col.operator("mesh.primitive_cube_add", text="Cube", icon="MESH_CUBE")
#col.operator("mesh.primitive_monkey_add", text="Monkey", icon="MESH_MONKEY")
col = self .layout .column ( align = True )
col.prop ( context.scene , "a_string_prop" )
col.prop ( context.scene , "b_string_prop" )
col.prop ( context.scene , "r_string_prop" )
row = layout.row ( )
row.operator ( "calculator.add" , text= "Add two numbers" )
class MyOperator( bpy.types .Operator ) :
bl_idname = "calculator.add"
bl_label = "Simple operator"
bl_description = "My Operator"
def execute( self , context) :
a = context.scene .a_string_prop
b = context.scene .b_string_prop
r = float ( a) + float ( b)
context.scene .r_string_prop = str ( r)
return { 'FINISHED' }
def register( ) :
bpy.utils .register_class ( MyPanel)
bpy.utils .register_class ( MyOperator)
bpy.types .Scene .a_string_prop = bpy.props .StringProperty \
(
name = "a" ,
description = "First operand"
)
bpy.types .Scene .b_string_prop = bpy.props .StringProperty \
(
name = "b" ,
description = "Second operand"
)
bpy.types .Scene .r_string_prop = bpy.props .StringProperty \
(
name = "result" ,
description = "Result operand"
)
def unregister( ) :
bpy.utils .unregister_class ( MyPanel)
bpy.utils .unregister_class ( MyOperator)
del bpy.types .Scene .a_string_prop
del bpy.types .Scene .b_string_prop
del bpy.types .Scene .r_string_prop
if __name__ == "__main__" :
register( )
Объединение урока Артёма Слаквы об добавлении GUI с примером генерации 2D сетки
2d_grid_with_gui.py
Python 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
bl_info = {
"name" : "Generate" ,
"category" : "3D View"
}
import bpy
name = 'Gridtastic'
rows = 5
size = 1
def vert( column, row) :
return ( column * size, row * size, 0 )
def face( column, row) :
global rows
global size
return ( column * rows + row,
column * rows + 1 + row,
( column + 1 ) * rows + 1 + row,
( column + 1 ) * rows + row)
class MyPanel( bpy.types .Panel ) :
bl_label = "2D Grid"
bl_space_type = 'VIEW_3D'
bl_region_type = 'TOOLS'
def draw( self , context) :
layout = self .layout
col = self .layout .column ( align = True )
col.prop ( context.scene , "rows_string_prop" )
col.prop ( context.scene , "cols_string_prop" )
col.prop ( context.scene , "size_string_prop" )
row = layout.row ( )
row.operator ( "generator.grid" , text= "Generate" )
class MyOperator( bpy.types .Operator ) :
bl_idname = "generator.grid"
bl_label = "Generator"
bl_description = "My Operator"
def execute( self , context) :
rows = int ( context.scene .rows_string_prop )
columns = int ( context.scene .cols_string_prop )
size = float ( context.scene .size_string_prop )
verts = [ vert( x, y) for x in range ( columns) for y in range ( rows) ]
faces = [ face( x, y) for x in range ( columns - 1 ) for y in range ( rows - 1 ) ]
mesh = bpy.data .meshes .new ( name)
mesh.from_pydata ( verts, [ ] , faces)
obj = bpy.data .objects .new ( name, mesh)
bpy.context .scene .objects .link ( obj)
bpy.context .scene .objects .active = obj
obj.select = True
return { 'FINISHED' }
def register( ) :
bpy.utils .register_class ( MyPanel)
bpy.utils .register_class ( MyOperator)
bpy.types .Scene .rows_string_prop = bpy.props .StringProperty ( name = "rows" )
bpy.types .Scene .cols_string_prop = bpy.props .StringProperty ( name = "cols" )
bpy.types .Scene .size_string_prop = bpy.props .StringProperty ( name = "size" )
def unregister( ) :
bpy.utils .unregister_class ( MyPanel)
bpy.utils .unregister_class ( MyOperator)
del bpy.types .Scene .rows_string_prop
del bpy.types .Scene .cols_string_prop
del bpy.types .Scene .size_string_prop
if __name__ == "__main__" :
register( )
Бесплатная онлайн-книга: https://web.archive.org/web/20... e_snippets
Прикрепил эту книгу в виде PDF и HTML-версии откуда код можно скопировать на случай если ссылки перестанут быть доступными. Прикрепил код в отдельном архиве. В книгу добавил содержание в виде закладок с помощью бесплатного PDF редактора-просмоторщика PDF-XChange Viewer.
Animation Blender Docs
Для моего ноутбука Asus K53SV оптимальной версией Blender оказалась версия 2.67b , а уже с версией 2.68 ноутбук начинает шуметь.