Форум программистов, компьютерный форум, киберфорум
8Observer8
Войти
Регистрация
Восстановить пароль
Карта форума Блоги Сообщество Поиск Заказать работу  
Рейтинг: 5.00. Голосов: 3.

Генерация логотипа Mitsubishi на Python в Blender 2.8. Загрузка логотипа в C# OpenGL 3 из Collada (.dae) формата

Запись от 8Observer8 размещена 11.02.2020 в 13:34
Обновил(-а) 8Observer8 11.05.2023 в 01:35 (Исправил "мешей на Blender 2.7 и 2.8" на "мешей на Blender 2.7 и 2.9")

Содержание блога

Скрипт на 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



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()


Название: 122d391d-3093-4e9d-badf-b15839aeddc6.png
Просмотров: 678

Размер: 4.2 Кб


Объединение урока Артёма Слаквы об добавлении GUI с примером генерации 2D сетки

Название: 01dd6bfb-ff30-4e29-b2f2-52b6557933e2.png
Просмотров: 651

Размер: 3.6 Кб

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 ноутбук начинает шуметь.
Изображения
 
Вложения
Тип файла: zip mitsubishi_logo_blender_python.zip (6.0 Кб, 862 просмотров)
Тип файла: zip MitsubishiLogo_OpenTkOpenGL30CSharp.zip (3.03 Мб, 996 просмотров)
Тип файла: zip 2011 - 04 - Code Snippets for Blender 257 - Thomas Larsson - HTML Version.zip (7.26 Мб, 85 просмотров)
Тип файла: pdf 2011 - Code Snippets for Blender 258 - Thomas Larsson.pdf (4.78 Мб, 1327 просмотров)
Тип файла: zip 2011 - 04 - Code Snippets for Blender 257 - Thomas Larsson - Code.zip (69.6 Кб, 88 просмотров)
Тип файла: zip blender_python_reference_2_67_release.zip (4.31 Мб, 52 просмотров)
Размещено в Без категории
Показов 6940 Комментарии 13
Всего комментариев 13
Комментарии
  1. Старый комментарий
    Аватар для 8Observer8
    Qt поддерживает XPath 2.0. Проблема с пространствами имён в Collada (.dae) файле решается с помощью локальных имён.

    Пример (источник)

    C++ (Qt)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
        QString planePath = ":/Models/Plane.dae";
        QFile f(planePath);
        if (!f.open(QIODevice::ReadOnly))
        {
            std::cerr << "Failed to load the file: " <<
                         planePath.toStdString() << std::endl;
            return;
        }
     
        QXmlQuery query;
        query.bindVariable("myFile", &f);
    //    query.setQuery("doc($myFile)//*[local-name() = 'p']/text()"); // it works too but it is XPath 1.0
        query.setQuery("doc($myFile)//*:p/text()");
     
        QString result;
        query.evaluateTo(&result);
        qDebug() << result;
        f.close();
    Запись от 8Observer8 размещена 06.08.2020 в 20:59 8Observer8 вне форума
  2. Старый комментарий
    Аватар для 8Observer8
    Вывод программы:

    Цитата:
    "1 0 0 2 0 1 0 0 2 1 0 3 3 0 4 2 0 5\n"
    Входной файл:

    Plane.dae

    XML
    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
    
    <?xml version="1.0" encoding="utf-8"?>
    <COLLADA xmlns="http://www.collada.org/2005/11/COLLADASchema" version="1.4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <asset>
        <contributor>
          <author>Blender User</author>
          <authoring_tool>Blender 2.83.3 commit date:2020-07-22, commit time:06:01, hash:353e5bd7493e</authoring_tool>
        </contributor>
        <created>2020-08-03T14:03:19</created>
        <modified>2020-08-03T14:03:19</modified>
        <unit name="meter" meter="1"/>
        <up_axis>Z_UP</up_axis>
      </asset>
      <library_effects>
        <effect id="PlaneMaterial-effect">
          <profile_COMMON>
            <technique sid="common">
              <lambert>
                <emission>
                  <color sid="emission">0 0 0 1</color>
                </emission>
                <diffuse>
                  <color sid="diffuse">0.01664001 0.8000001 0.01191879 1</color>
                </diffuse>
                <reflectivity>
                  <float sid="specular">0.5</float>
                </reflectivity>
              </lambert>
            </technique>
          </profile_COMMON>
        </effect>
      </library_effects>
      <library_images/>
      <library_materials>
        <material id="PlaneMaterial-material" name="PlaneMaterial">
          <instance_effect url="#PlaneMaterial-effect"/>
        </material>
      </library_materials>
      <library_geometries>
        <geometry id="Plane-mesh" name="Plane">
          <mesh>
            <source id="Plane-mesh-positions">
              <float_array id="Plane-mesh-positions-array" count="12">-1 -1 0 1 -1 0 -1 1 0 1 1 0</float_array>
              <technique_common>
                <accessor source="#Plane-mesh-positions-array" count="4" stride="3">
                  <param name="X" type="float"/>
                  <param name="Y" type="float"/>
                  <param name="Z" type="float"/>
                </accessor>
              </technique_common>
            </source>
            <source id="Plane-mesh-normals">
              <float_array id="Plane-mesh-normals-array" count="3">0 0 1</float_array>
              <technique_common>
                <accessor source="#Plane-mesh-normals-array" count="1" stride="3">
                  <param name="X" type="float"/>
                  <param name="Y" type="float"/>
                  <param name="Z" type="float"/>
                </accessor>
              </technique_common>
            </source>
            <source id="Plane-mesh-map-0">
              <float_array id="Plane-mesh-map-0-array" count="12">1 0 0 1 0 0 1 0 1 1 0 1</float_array>
              <technique_common>
                <accessor source="#Plane-mesh-map-0-array" count="6" stride="2">
                  <param name="S" type="float"/>
                  <param name="T" type="float"/>
                </accessor>
              </technique_common>
            </source>
            <vertices id="Plane-mesh-vertices">
              <input semantic="POSITION" source="#Plane-mesh-positions"/>
            </vertices>
            <triangles material="PlaneMaterial-material" count="2">
              <input semantic="VERTEX" source="#Plane-mesh-vertices" offset="0"/>
              <input semantic="NORMAL" source="#Plane-mesh-normals" offset="1"/>
              <input semantic="TEXCOORD" source="#Plane-mesh-map-0" offset="2" set="0"/>
              <p>1 0 0 2 0 1 0 0 2 1 0 3 3 0 4 2 0 5</p>
            </triangles>
          </mesh>
        </geometry>
      </library_geometries>
      <library_visual_scenes>
        <visual_scene id="Scene" name="Scene">
          <node id="Plane" name="Plane" type="NODE">
            <matrix sid="transform">1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1</matrix>
            <instance_geometry url="#Plane-mesh" name="Plane">
              <bind_material>
                <technique_common>
                  <instance_material symbol="PlaneMaterial-material" target="#PlaneMaterial-material">
                    <bind_vertex_input semantic="UVMap" input_semantic="TEXCOORD" input_set="0"/>
                  </instance_material>
                </technique_common>
              </bind_material>
            </instance_geometry>
          </node>
        </visual_scene>
      </library_visual_scenes>
      <scene>
        <instance_visual_scene url="#Scene"/>
      </scene>
    </COLLADA>
    Запись от 8Observer8 размещена 06.08.2020 в 20:59 8Observer8 вне форума
    Обновил(-а) 8Observer8 06.08.2020 в 21:02
  3. Старый комментарий
    Аватар для 8Observer8
    Aналог Qt C++ кода выше на TypeScript.

    Песочница

    main.ts

    Javascript
    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
    
    function loadFile(path: string, cb: (content: Document) => void)
    {
        const request = new XMLHttpRequest();
        request.onreadystatechange =
            () =>
            {
                if (request.readyState === 4 && request.status !== 200)
                {
                    console.log(`Faield to load the file: "${path}"`);
                    return;
                }
            }
        request.onload =
            () =>
            {
                cb(request.responseXML);
            };
        request.open("GET", path, true);
        request.send();
    }
     
    function main()
    {
        // const partOfId = "mesh-positions-array";
        // const exp = `//float_array[substring(@id, string-length(@id) -` +
        //     `string-length('${partOfId}') + 1) = '${partOfId}']`;
        const exp = "//*[local-name() = 'p']/text()";
     
        loadFile("./plane.dae",
            (content: Document) =>
            {
                const nodes = content.evaluate(
                    exp, content,
                    null, XPathResult.ANY_TYPE, null);
                const result = nodes.iterateNext();
                console.log(result);
            });
    }
     
    main();
    Запись от 8Observer8 размещена 22.08.2020 в 20:01 8Observer8 вне форума
    Обновил(-а) 8Observer8 22.08.2020 в 20:10
  4. Старый комментарий
    Аватар для 8Observer8
    Координаты из XML на TypeScript. Код выше извлекает индексы треугольников с помощью языка XPath и TypeScript, а код ниже извлекает координаты треугольников в массив.

    Песочница

    main.ts

    Javascript
    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
    
    function loadFile(path: string, cb: (content: Document) => void)
    {
        const request = new XMLHttpRequest();
        request.onreadystatechange =
            () =>
            {
                if (request.readyState === 4 && request.status !== 200)
                {
                    console.log(`Faield to load the file: "${path}"`);
                    return;
                }
            }
        request.onload =
            () =>
            {
                cb(request.responseXML);
            };
        request.open("GET", path, true);
        request.send();
    }
     
    function main()
    {
        const partOfId = "mesh-positions-array";
        const exp = `//*[local-name() = 'float_array'][substring(@id, string-length(@id) -` +
            `string-length('${partOfId}') + 1) = '${partOfId}']`;
     
        loadFile("./plane.dae",
            (content: Document) =>
            {
                const nodes = content.evaluate(
                    exp, content,
                    null, XPathResult.ANY_TYPE, null);
                const result = nodes.iterateNext();
                console.log(result.textContent);
                const positions = result.textContent.split(" ").map(
                    (value) => { return parseFloat(value); });
                console.log(positions);
            });
    }
     
    main();
    Запись от 8Observer8 размещена 22.08.2020 в 22:21 8Observer8 вне форума
    Обновил(-а) 8Observer8 16.12.2021 в 13:57
  5. Старый комментарий
    Аватар для 8Observer8
    Перевод кода Qt C++ на Python (PyQt5)

    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
    
    import sys
    from PyQt5.QtCore import QBuffer, QByteArray, QFile, QIODevice, QTextCodec
    from PyQt5.QtXmlPatterns import QXmlQuery
    from PyQt5.QtWidgets import QApplication, QTextEdit, QVBoxLayout, QWidget
     
     
    class Window(QWidget):
        def __init__(self):
            super().__init__()
            self.resize(250, 250)
            output = QTextEdit()
            lay = QVBoxLayout(self)
            lay.addWidget(output)
     
            planePath = "./Plane.dae"
            f = QFile(planePath)
            if not f.open(QIODevice.ReadOnly):
                print("Falied to load the file:", planePath)
            query = QXmlQuery()
            query.bindVariable("myFile", f)
            query.setQuery("doc($myFile)//*:p/text()")
     
            ba = QByteArray()
            buf = QBuffer(ba)
            buf.open(QIODevice.ReadWrite)
     
            query.evaluateTo(buf)
     
            text = QTextCodec.codecForName("UTF-8").toUnicode(ba)
            output.setText(text)
     
     
    def main():
        app = QApplication(sys.argv)
        w = Window()
        w.show()
        sys.exit(app.exec_())
     
     
    if __name__ == "__main__":
        main()
    [Добавлено 17.04.2022]

    Этот код:

    Python
    1
    2
    3
    4
    5
    6
    7
    
            ba = QByteArray()
            buf = QBuffer(ba)
            buf.open(QIODevice.ReadWrite)
     
            query.evaluateTo(buf)
     
            text = QTextCodec.codecForName("UTF-8").toUnicode(ba)
    можно заменить одной строкой:

    Python
    1
    
            text = query.evaluateToString()
    Запись от 8Observer8 размещена 23.08.2020 в 01:05 8Observer8 вне форума
    Обновил(-а) 8Observer8 17.04.2022 в 02:15
  6. Старый комментарий
    Аватар для 8Observer8
    Аналог на JavaScript (ES6)

    Песочница

    PHP/HTML
    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
    
    <html>
     
    <body>
        <p>See the console output (Ctrl+Shift+J in Chrome)</p>
     
        <script>
            function loadFile(path, cb) {
                const request = new XMLHttpRequest();
                request.onreadystatechange = () => {
                    if (request.readyState === 4 && request.status !== 200) {
                        console.log(`Faield to load the file: "${path}"`);
                        return;
                    }
                };
                request.onload = () => {
                    cb(request.responseXML);
                };
                request.open('GET', path, true);
                request.send();
            }
     
            function main() {
                const partOfId = 'mesh-positions-array';
                const exp =
                    `//*[local-name() = 'float_array'][substring(@id, string-length(@id) -` +
                    `string-length('${partOfId}') + 1) = '${partOfId}']`;
                // const exp = "//*[local-name() = 'p']/text()";
     
                loadFile('./plane.dae', (content) => {
                    const nodes = content.evaluate(
                        exp,
                        content,
                        null,
                        XPathResult.ANY_TYPE,
                        null
                    );
                    const result = nodes.iterateNext();
                    console.log(result.textContent);
                    const positions = result.textContent.split(' ').map(value => {
                        return parseFloat(value);
                    });
                    console.log(positions);
                });
            }
     
            main();
        </script>
    </body>
     
    </html>
    Запись от 8Observer8 размещена 23.08.2020 в 11:26 8Observer8 вне форума
  7. Старый комментарий
    Аватар для 8Observer8
    XPath-парсинг координат из DAE и загрузка в VBO

    Javascript
    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
    
    import FileLoader from "./FileLoader";
    import { gl } from "./WebGLContext";
     
    export default class DaeLoader
    {
        public static Load(fileName: string, cb: (vertPosBuffer: WebGLBuffer) => void): void
        {
            const path = "models/" + fileName;
            const expForIndices = "//*[local-name() = 'p']/text()";
            const expForVertPositions =
                "//*[local-name() = 'float_array'][substring(@id, " +
                "string-length(@id) - string-length('positions-array') + 1) = 'positions-array']";
     
            FileLoader.LoadXML(path,
                (doc) =>
                {
                    const indexNodes = doc.evaluate(expForIndices, doc, null, XPathResult.ANY_TYPE, null);
                    const indices = indexNodes.iterateNext().textContent
                        .split(" ")
                        .map(item => { return parseInt(item) });
                    const indicesForVertPos = indices.filter((value, index) => { return index % 2 === 0; });
                    const amountOfVertices = indicesForVertPos.length;
     
                    const vertPosDataNodes = doc.evaluate(expForVertPositions, doc,
                        null, XPathResult.ANY_TYPE, null);
                    const vertPosData = vertPosDataNodes.iterateNext().textContent
                        .split(" ")
                        .map(item => { return parseFloat(item); });
     
                    const vertPositions: number[] = [];
                    for (let i = 0; i < amountOfVertices; i++)
                    {
                        const index = indicesForVertPos[i];
                        const x = vertPosData[index + 0];
                        const y = vertPosData[index + 1];
                        const z = vertPosData[index + 2];
                        vertPositions.push(x, y, z);
                    }
     
                    const vertPosBuffer = gl.createBuffer();
                    gl.bindBuffer(gl.ARRAY_BUFFER, vertPosBuffer);
                    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertPositions), gl.STATIC_DRAW);
     
                    cb(vertPosBuffer);
                });
        }
    }
    Запись от 8Observer8 размещена 06.09.2020 в 00:24 8Observer8 вне форума
    Обновил(-а) 8Observer8 06.09.2020 в 00:27
  8. Старый комментарий
    Аватар для 8Observer8
    Пример из первого урока: 2D сетка на Blender 2.8 API (в туториале применяется Blender 2.79). Автор оригинала переправил на 2.8: The 2D Grid

    Python
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    # Settings
    name = "Gridtastic"
    rows = 5
    columns = 10
     
    verts = []
    faces = []
     
    # 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)
    Для сравнения, Blender 2.79:

    Python
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    verts = []
    faces = []
     
    # 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
    Запись от 8Observer8 размещена 29.11.2020 в 21:37 8Observer8 вне форума
    Обновил(-а) 8Observer8 29.11.2020 в 21:50
  9. Старый комментарий
    Аватар для 8Observer8
    Загрузка данных 3D-модели из XML на Qt C++

    C++ (Qt)
    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
    
    // Add these lines to the .pro file:
    // QT += xml
    // win32: LIBS += -lopengl32
     
    #include <QtWidgets/QApplication>
    #include <QtWidgets/QOpenGLWidget>
    #include <QtXml/QDomDocument>
    #include <QtCore/QFile>
    #include <QtCore/QDebug>
     
    struct DaeData {
        QString vertices;
        QString indices;
    };
     
    class OpenGLWidget : public QOpenGLWidget {
    public:
        OpenGLWidget(QWidget *parent = nullptr) : QOpenGLWidget(parent) {}
    private:
        int m_amountOfVertices;
        void initializeGL() override {
            DaeData data = getDaeData();
            qDebug() << data.vertices;
            qDebug() << data.indices;
            m_amountOfVertices = initVertexBuffers();
        }
        void resizeGL(int w, int h) override {
            glViewport(0, 0, w, h);
        }
        void paintGL() override {
            glClear(GL_COLOR_BUFFER_BIT);
        }
        int initVertexBuffers() {
     
        }
        DaeData getDaeData() {
            DaeData daeData;
            QDomDocument xmlDoc;
            QFile f(":/Models/Plane.dae");
            if (!f.open(QIODevice::ReadOnly)) {
                qDebug() << "Failed to open file.";
            }
            xmlDoc.setContent(&f);
            f.close();
            QDomElement root = xmlDoc.documentElement();
            QDomElement daeElem = root.firstChildElement();
            while(!daeElem.isNull()) {
                if (daeElem.tagName() == "library_geometries") {
                    QDomElement geomElem = daeElem.firstChildElement();
                    if (geomElem.tagName() == "geometry") {
                        QDomElement meshElem = geomElem.firstChildElement();
                        if (meshElem.tagName() == "mesh") {
                            QDomElement meshChildElem = meshElem.firstChildElement();
                            while(!meshChildElem.isNull()) {
                                if (meshChildElem.attribute("id") == "Plane-mesh-positions") {
                                    QDomElement floatArrayElem = meshChildElem.firstChildElement();
                                    QString strPositions = floatArrayElem.firstChild().toText().data();
                                    daeData.vertices = strPositions;
                                }
                                if (meshChildElem.tagName() == "triangles") {
                                    QDomElement trianglesChildElem = meshChildElem.firstChildElement();
                                    while(!trianglesChildElem.isNull()) {
                                        if (trianglesChildElem.tagName() == "p") {
                                            QString strAllIndices = trianglesChildElem.firstChild().toText().data();
                                            daeData.indices = strAllIndices;
                                        }
                                        trianglesChildElem = trianglesChildElem.nextSiblingElement();
                                    }
                                }
                                meshChildElem = meshChildElem.nextSiblingElement();
                            }
                        }
                    }
                }
                daeElem = daeElem.nextSiblingElement();
            }
            return daeData;
        }
    };
     
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        OpenGLWidget w;
        w.show();
        return a.exec();
    }
    Запись от 8Observer8 размещена 15.12.2020 в 15:25 8Observer8 вне форума
  10. Старый комментарий
    Аватар для 8Observer8
    Загрузка данных 3D-модели из XML на JavaScript

    Вывод программы для плоскости:

    Цитата:
    Indices = 1 0 0 2 0 1 0 0 2 1 0 3 3 0 4 2 0 5
    Vectex Coords: -1 -1 0 1 -1 0 -1 1 0 1 1 0
    Normals: 0 0 1
    Texture Coords: 1 0 0 1 0 0 1 0 1 1 0 1
    PHP/HTML
    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
    
    <!DOCTYPE html>
    <html lang="en">
     
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Load Data Of 3D Model From Dae. WebGL 1.0, JavaScript</title>
    </head>
     
    <body>
        <script>
     
            loadXmlFile("assets/models/Plane.dae",
                (doc) =>
                {
                    const indices = doc.getElementsByTagName("p")[0].childNodes[0].nodeValue;
                    console.log("Indices = ", indices);
                    const floatArrays = doc.getElementsByTagName("float_array");
                    for (let i = 0; i < floatArrays.length; i++)
                    {
                        const id = floatArrays[i].getAttribute("id");
                        if (/mesh-positions-array$/.test(id))
                        {
                            console.log("Vectex Coords: ", floatArrays[i].childNodes[0].nodeValue);
                        }
                        else if (/mesh-normals-array$/.test(id))
                        {
                            console.log("Normals: ", floatArrays[i].childNodes[0].nodeValue);
                        }
                        else if (/mesh-map-0-array$/.test(id))
                        {
                            console.log("Texture Coords: ", floatArrays[i].childNodes[0].nodeValue);
                        }
                    }
                });
     
            function loadXmlFile(path, cb)
            {
                const req = new XMLHttpRequest();
                req.onreadystatechange =
                    () =>
                    {
                        if (req.readyState === 4 && req.status == 200)
                        {
                            cb(req.responseXML);
                        }
                    };
                req.open("GET", path);
                req.send();
            }
        </script>
    </body>
     
    </html>
    Запись от 8Observer8 размещена 15.12.2020 в 19:05 8Observer8 вне форума
    Обновил(-а) 8Observer8 15.12.2020 в 19:08
  11. Старый комментарий
    Аватар для 8Observer8
    В бесплатную книгу Thomas Larsson добавил содержание в виде закладок с помощью бесплатного PDF редактора-просмоторщика PDF-XChange Viewer.
    Запись от 8Observer8 размещена 01.03.2021 в 15:14 8Observer8 вне форума
    Обновил(-а) 8Observer8 01.03.2021 в 15:15
  12. Старый комментарий
    Аватар для 8Observer8
    Отдельным архивом прикрепил код из книги Thomas Larsson.
    Запись от 8Observer8 размещена 01.03.2021 в 15:15 8Observer8 вне форума
  13. Старый комментарий
    Аватар для 8Observer8
    Отличие в чтении JSON на PyQt6 и PySide6. На PyQt6 нужно вызывать метод toDouble(), а на PySide6 - не нужно:

    PyQt6:

    Python
    1
    
    gravity = root["gravity"].toDouble()
    PySide6:

    Python
    1
    
    gravity = root["gravity"]
    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
    
    import sys
     
    from PyQt6.QtCore import QByteArray, QJsonDocument
    from PyQt6.QtWidgets import QApplication, QWidget
     
     
    class Window(QWidget):
     
        def __init__(self):
            super().__init__()
     
            content = "{ \"gravity\": -9.8 }"
            doc = QJsonDocument.fromJson(QByteArray(content.encode()))
            root = doc.object()
            gravity = root["gravity"].toDouble() # PyQt6
            # gravity = root["gravity"] # PySide6
            print("gravity =", gravity)
            print(type(gravity))
     
    if __name__ == "__main__":
        app = QApplication(sys.argv)
        w = Window()
        w.show()
        sys.exit(app.exec())
    Запись от 8Observer8 размещена 23.04.2023 в 18:00 8Observer8 вне форума
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru